ucn 5.5.1 → 5.5.3
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.
- package/.claude/skills/ucn/SKILL.md +35 -9
- package/.claude/skills/ucn/references/commands.md +40 -18
- package/.claude/skills/ucn/references/resolution-rules.md +126 -20
- package/.claude/skills/ucn/references/trust-contract.md +5 -3
- package/README.md +32 -21
- package/cli/index.js +2 -1
- package/core/accessors.js +341 -54
- package/core/account.js +74 -20
- package/core/analysis.js +257 -125
- package/core/ast-analysis.js +12 -3
- package/core/bridge.js +15 -65
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +137 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +4726 -940
- package/core/check.js +22 -5
- package/core/class-identity.js +399 -12
- package/core/client-receivers.js +189 -0
- package/core/constructor-calls.js +831 -0
- package/core/constructor-hierarchy.js +147 -0
- package/core/constructor-protocol.js +196 -0
- package/core/contract-membership.js +346 -140
- package/core/cpp-conversion-constraints.js +123 -0
- package/core/cpp-conversion-syntax.js +216 -0
- package/core/cpp-conversions.js +414 -0
- package/core/cpp-scope.js +245 -44
- package/core/deadcode.js +61 -16
- package/core/decorator-binding.js +137 -3
- package/core/enum-reference-syntax.js +134 -0
- package/core/execute.js +17 -1
- package/core/external-macros.js +42 -10
- package/core/go-packages.js +171 -0
- package/core/graph-build.js +2 -0
- package/core/graph.js +75 -59
- package/core/ignore-rules.js +2 -1
- package/core/imports.js +145 -19
- package/core/index-ir.js +41 -4
- package/core/inprocess-clients.js +41 -15
- package/core/ir.js +27 -6
- package/core/macro-expansion.js +38 -2
- package/core/out-of-class-members.js +182 -0
- package/core/output/analysis-ext.js +4 -2
- package/core/output/analysis.js +89 -20
- package/core/output/graph.js +12 -3
- package/core/output/lines.js +4 -3
- package/core/output/public.js +2 -0
- package/core/output/reporting.js +12 -1
- package/core/output/shared.js +2 -0
- package/core/output/tracing.js +28 -22
- package/core/output-budget.js +2 -2
- package/core/parallel-build.js +52 -1
- package/core/project.js +217 -112
- package/core/provenance-constructor-base.js +161 -0
- package/core/provenance-facts.js +257 -27
- package/core/provenance-overload.js +458 -54
- package/core/provenance-type.js +487 -0
- package/core/provenance.js +234 -15
- package/core/public-command.js +17 -2
- package/core/query-bodies.js +159 -0
- package/core/reflection.js +270 -2
- package/core/rename-token-syntax.js +111 -0
- package/core/reporting.js +13 -7
- package/core/request-helpers.js +63 -7
- package/core/rust-macro-expansion.js +151 -19
- package/core/rust-modules.js +1 -1
- package/core/rust-names.js +607 -0
- package/core/search.js +70 -17
- package/core/shared.js +98 -1
- package/core/source-memo.js +201 -0
- package/core/text-usage-syntax.js +38 -0
- package/core/tracing.js +7 -1
- package/core/type-denotation.js +124 -37
- package/core/type-references.js +615 -246
- package/core/value-references.js +2777 -0
- package/core/verify.js +1097 -281
- package/languages/adapter.js +28 -4
- package/languages/c-family.js +1118 -164
- package/languages/constructor-bases.js +105 -0
- package/languages/cpp-platform.js +26 -0
- package/languages/cpp-site-syntax.js +83 -0
- package/languages/csharp-scope.js +439 -0
- package/languages/csharp.js +428 -68
- package/languages/go.js +385 -207
- package/languages/html.js +4 -2
- package/languages/index.js +397 -2
- package/languages/java-platform.js +226 -0
- package/languages/java-unicode.js +269 -0
- package/languages/java.js +461 -117
- package/languages/javascript.js +418 -66
- package/languages/lexical-scope.js +190 -23
- package/languages/python.js +404 -54
- package/languages/rust.js +510 -82
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +3 -3
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* languages/java-platform.js - JDK contracts the Java overload, conversion
|
|
5
|
+
* and receiver rules read (fix #421: moved out of core/callers.js). Each
|
|
6
|
+
* table is a specification-level fact of the Java platform (JLS conversions,
|
|
7
|
+
* final value types, java.lang interface members, factory return types), not
|
|
8
|
+
* knowledge about any project. A platform type outside these tables has
|
|
9
|
+
* unknown members and supertypes: the engine's answer through it stays open
|
|
10
|
+
* (unverified with a reason), never closed by assumption.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const JAVA_FINAL_REFERENCE_TYPES = new Set([
|
|
14
|
+
'String', 'Class', 'Boolean', 'Byte', 'Character', 'Short',
|
|
15
|
+
'Integer', 'Long', 'Float', 'Double', 'Void',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
// Complete reference-supertype sets for JDK value types (fix #299D): these
|
|
19
|
+
// types are final (or effectively closed) with a fixed, fully-known
|
|
20
|
+
// ancestry, so an argument statically typed as one of them can bind ONLY a
|
|
21
|
+
// parameter type in this list (plus Object/generics, handled earlier). A
|
|
22
|
+
// project class shadowing one of these simple names disables the denial —
|
|
23
|
+
// the guard is tDefs.length === 0 at the use site. Modern-JDK marker
|
|
24
|
+
// interfaces (Constable, ConstantDesc) are included so their params never
|
|
25
|
+
// get falsely denied.
|
|
26
|
+
const JAVA_PLATFORM_VALUE_ANCESTRY = new Map([
|
|
27
|
+
['Integer', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
28
|
+
['Long', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
29
|
+
['Short', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
30
|
+
['Byte', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
31
|
+
['Float', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
32
|
+
['Double', ['Number', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
33
|
+
['Character', ['Comparable', 'Serializable', 'Constable']],
|
|
34
|
+
['Boolean', ['Comparable', 'Serializable', 'Constable']],
|
|
35
|
+
['String', ['CharSequence', 'Comparable', 'Serializable', 'Constable', 'ConstantDesc']],
|
|
36
|
+
['BigInteger', ['Number', 'Comparable', 'Serializable']],
|
|
37
|
+
['BigDecimal', ['Number', 'Comparable', 'Serializable']],
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
// Unboxing is defined for exactly these wrapper types (JLS 5.1.8).
|
|
41
|
+
const JAVA_WRAPPER_PRIMITIVES = new Map([
|
|
42
|
+
['Integer', 'int'], ['Long', 'long'], ['Short', 'short'], ['Byte', 'byte'],
|
|
43
|
+
['Character', 'char'], ['Boolean', 'boolean'],
|
|
44
|
+
['Float', 'float'], ['Double', 'double'],
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
// Declared types of the JDK numeric-constant fields (fix #299D): exact
|
|
48
|
+
// compiler contracts, consulted only when no project field shadows the
|
|
49
|
+
// owner/field pair.
|
|
50
|
+
const JAVA_PLATFORM_FIELD_TYPES = new Map([
|
|
51
|
+
['Float.NaN', 'float'], ['Float.POSITIVE_INFINITY', 'float'],
|
|
52
|
+
['Float.NEGATIVE_INFINITY', 'float'], ['Float.MIN_VALUE', 'float'],
|
|
53
|
+
['Float.MAX_VALUE', 'float'], ['Float.MIN_NORMAL', 'float'],
|
|
54
|
+
['Double.NaN', 'double'], ['Double.POSITIVE_INFINITY', 'double'],
|
|
55
|
+
['Double.NEGATIVE_INFINITY', 'double'], ['Double.MIN_VALUE', 'double'],
|
|
56
|
+
['Double.MAX_VALUE', 'double'], ['Double.MIN_NORMAL', 'double'],
|
|
57
|
+
['Integer.MAX_VALUE', 'int'], ['Integer.MIN_VALUE', 'int'],
|
|
58
|
+
['Long.MAX_VALUE', 'long'], ['Long.MIN_VALUE', 'long'],
|
|
59
|
+
['Short.MAX_VALUE', 'short'], ['Short.MIN_VALUE', 'short'],
|
|
60
|
+
['Byte.MAX_VALUE', 'byte'], ['Byte.MIN_VALUE', 'byte'],
|
|
61
|
+
['Character.MAX_VALUE', 'char'], ['Character.MIN_VALUE', 'char'],
|
|
62
|
+
['Boolean.TRUE', 'Boolean'], ['Boolean.FALSE', 'Boolean'],
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
// Small, authoritative platform surface used only when the parser preserved
|
|
66
|
+
// the imported owner. These are JDK interface/method declarations, not
|
|
67
|
+
// name-based guesses. They close the common portable-AST gap where overload
|
|
68
|
+
// resolution depends on the return type of a library call.
|
|
69
|
+
const JAVA_PLATFORM_METHOD_RETURNS = new Map([
|
|
70
|
+
// Exact JDK factory contracts (fix #299D): valueOf on the boxed and
|
|
71
|
+
// arbitrary-precision types returns the owner type itself.
|
|
72
|
+
['java.math.BigInteger#valueOf', 'java.math.BigInteger'],
|
|
73
|
+
['java.math.BigDecimal#valueOf', 'java.math.BigDecimal'],
|
|
74
|
+
['java.lang.Integer#valueOf', 'java.lang.Integer'],
|
|
75
|
+
['java.lang.Long#valueOf', 'java.lang.Long'],
|
|
76
|
+
['java.lang.Short#valueOf', 'java.lang.Short'],
|
|
77
|
+
['java.lang.Byte#valueOf', 'java.lang.Byte'],
|
|
78
|
+
['java.lang.Float#valueOf', 'java.lang.Float'],
|
|
79
|
+
['java.lang.Double#valueOf', 'java.lang.Double'],
|
|
80
|
+
['java.lang.Boolean#valueOf', 'java.lang.Boolean'],
|
|
81
|
+
['java.lang.Character#valueOf', 'java.lang.Character'],
|
|
82
|
+
['java.lang.String#valueOf', 'java.lang.String'],
|
|
83
|
+
['java.lang.Object#getClass', 'java.lang.Class'],
|
|
84
|
+
['java.lang.Class#getEnclosingClass', 'java.lang.Class'],
|
|
85
|
+
['java.lang.Class#getDeclaringClass', 'java.lang.Class'],
|
|
86
|
+
['java.lang.Class#getDeclaredMethod', 'java.lang.reflect.Method'],
|
|
87
|
+
['java.lang.Enum#getDeclaringClass', 'java.lang.Class'],
|
|
88
|
+
['java.lang.reflect.Method#getReturnType', 'java.lang.Class'],
|
|
89
|
+
['java.lang.reflect.Constructor#getDeclaringClass', 'java.lang.Class'],
|
|
90
|
+
['java.lang.annotation.Annotation#annotationType', 'java.lang.Class'],
|
|
91
|
+
['java.lang.reflect.Method#getGenericReturnType', 'java.lang.reflect.Type'],
|
|
92
|
+
['java.lang.reflect.Method#getParameterTypes', 'java.lang.Class[]'],
|
|
93
|
+
['java.lang.reflect.Method#getGenericParameterTypes', 'java.lang.reflect.Type[]'],
|
|
94
|
+
['javax.lang.model.element.Element#asType', 'javax.lang.model.type.TypeMirror'],
|
|
95
|
+
['javax.lang.model.element.TypeElement#asType', 'javax.lang.model.type.TypeMirror'],
|
|
96
|
+
['javax.lang.model.element.TypeParameterElement#asType', 'javax.lang.model.type.TypeMirror'],
|
|
97
|
+
['javax.lang.model.element.VariableElement#asType', 'javax.lang.model.type.TypeMirror'],
|
|
98
|
+
['javax.lang.model.element.ExecutableElement#asType', 'javax.lang.model.type.TypeMirror'],
|
|
99
|
+
['javax.lang.model.element.ExecutableElement#getReturnType', 'javax.lang.model.type.TypeMirror'],
|
|
100
|
+
['javax.lang.model.element.AnnotationMirror#getElementValues', 'java.util.Map'],
|
|
101
|
+
['javax.lang.model.element.Element#getAnnotationMirrors', 'java.util.List'],
|
|
102
|
+
['javax.lang.model.element.TypeElement#getAnnotationMirrors', 'java.util.List'],
|
|
103
|
+
['javax.lang.model.element.TypeElement#getEnclosedElements', 'java.util.List'],
|
|
104
|
+
['javax.lang.model.element.ExecutableElement#getParameters', 'java.util.List'],
|
|
105
|
+
['javax.lang.model.util.ElementFilter#methodsIn', 'java.util.List'],
|
|
106
|
+
['javax.lang.model.util.Types#getPrimitiveType', 'javax.lang.model.type.PrimitiveType'],
|
|
107
|
+
['javax.lang.model.util.Types#getArrayType', 'javax.lang.model.type.ArrayType'],
|
|
108
|
+
['javax.lang.model.util.Types#getDeclaredType', 'javax.lang.model.type.DeclaredType'],
|
|
109
|
+
['javax.lang.model.util.Types#getWildcardType', 'javax.lang.model.type.WildcardType'],
|
|
110
|
+
['javax.lang.model.util.Types#getNoType', 'javax.lang.model.type.NoType'],
|
|
111
|
+
['javax.lang.model.util.Types#getNullType', 'javax.lang.model.type.NullType'],
|
|
112
|
+
// JDK collection factories (fix #428): `with(Arrays.asList(..))` passes a
|
|
113
|
+
// List, never the element type of a sibling varargs overload.
|
|
114
|
+
['java.util.Arrays#asList', 'java.util.List'],
|
|
115
|
+
['java.util.List#of', 'java.util.List'], ['java.util.List#copyOf', 'java.util.List'],
|
|
116
|
+
['java.util.Set#of', 'java.util.Set'], ['java.util.Set#copyOf', 'java.util.Set'],
|
|
117
|
+
['java.util.Map#of', 'java.util.Map'], ['java.util.Map#copyOf', 'java.util.Map'],
|
|
118
|
+
['java.util.Collections#emptyList', 'java.util.List'], ['java.util.Collections#singletonList', 'java.util.List'],
|
|
119
|
+
['java.util.Collections#unmodifiableList', 'java.util.List'],
|
|
120
|
+
['java.util.Collections#emptySet', 'java.util.Set'], ['java.util.Collections#singleton', 'java.util.Set'],
|
|
121
|
+
['java.util.Collections#unmodifiableSet', 'java.util.Set'],
|
|
122
|
+
['java.util.Collections#emptyMap', 'java.util.Map'], ['java.util.Collections#singletonMap', 'java.util.Map'],
|
|
123
|
+
['java.util.Collections#unmodifiableMap', 'java.util.Map'],
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
// Complete inherited member-name surfaces of these JDK contracts. None declares
|
|
127
|
+
// a member type. A contract is usable only after the written supertype has
|
|
128
|
+
// resolved to this qualified SDK identity; an external namesake is unknown.
|
|
129
|
+
const JAVA_PLATFORM_ANCESTOR_MEMBERS = new Map([
|
|
130
|
+
['java.lang.Object', new Set(['getClass', 'hashCode', 'equals', 'clone',
|
|
131
|
+
'toString', 'notify', 'notifyAll', 'wait', 'finalize'])],
|
|
132
|
+
['java.lang.Comparable', new Set(['compareTo'])],
|
|
133
|
+
['java.lang.Iterable', new Set(['iterator', 'forEach', 'spliterator'])],
|
|
134
|
+
['java.lang.AutoCloseable', new Set(['close'])],
|
|
135
|
+
['java.io.Closeable', new Set(['close'])],
|
|
136
|
+
['java.util.Map.Entry', new Set(['getKey', 'getValue', 'setValue', 'equals', 'hashCode'])],
|
|
137
|
+
['java.lang.Runnable', new Set(['run'])],
|
|
138
|
+
['java.lang.Cloneable', new Set()],
|
|
139
|
+
['java.io.Serializable', new Set()],
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const JAVA_PLATFORM_SUPERTYPES = new Map([
|
|
143
|
+
['java.lang.reflect.Type', ['java.lang.Object']],
|
|
144
|
+
['java.lang.reflect.ParameterizedType', ['java.lang.Object', 'java.lang.reflect.Type']],
|
|
145
|
+
['java.lang.reflect.GenericArrayType', ['java.lang.Object', 'java.lang.reflect.Type']],
|
|
146
|
+
['java.lang.reflect.TypeVariable', ['java.lang.Object', 'java.lang.reflect.Type',
|
|
147
|
+
'java.lang.reflect.AnnotatedElement']],
|
|
148
|
+
['java.lang.reflect.WildcardType', ['java.lang.Object', 'java.lang.reflect.Type']],
|
|
149
|
+
['java.lang.Class', ['java.lang.Object', 'java.lang.reflect.Type', 'java.io.Serializable',
|
|
150
|
+
'java.lang.reflect.GenericDeclaration', 'java.lang.reflect.AnnotatedElement',
|
|
151
|
+
'java.lang.invoke.TypeDescriptor.OfField', 'java.lang.invoke.TypeDescriptor',
|
|
152
|
+
'java.lang.constant.Constable']],
|
|
153
|
+
['javax.lang.model.type.ArrayType', ['javax.lang.model.type.ReferenceType',
|
|
154
|
+
'javax.lang.model.type.TypeMirror']],
|
|
155
|
+
['javax.lang.model.type.DeclaredType', ['javax.lang.model.type.ReferenceType',
|
|
156
|
+
'javax.lang.model.type.TypeMirror']],
|
|
157
|
+
['javax.lang.model.type.ErrorType', ['javax.lang.model.type.DeclaredType',
|
|
158
|
+
'javax.lang.model.type.ReferenceType', 'javax.lang.model.type.TypeMirror']],
|
|
159
|
+
['javax.lang.model.type.ExecutableType', ['javax.lang.model.type.TypeMirror']],
|
|
160
|
+
['javax.lang.model.type.IntersectionType', ['javax.lang.model.type.TypeMirror']],
|
|
161
|
+
['javax.lang.model.type.NoType', ['javax.lang.model.type.TypeMirror']],
|
|
162
|
+
['javax.lang.model.type.NullType', ['javax.lang.model.type.ReferenceType',
|
|
163
|
+
'javax.lang.model.type.TypeMirror']],
|
|
164
|
+
['javax.lang.model.type.PrimitiveType', ['javax.lang.model.type.TypeMirror']],
|
|
165
|
+
['javax.lang.model.type.TypeVariable', ['javax.lang.model.type.ReferenceType',
|
|
166
|
+
'javax.lang.model.type.TypeMirror']],
|
|
167
|
+
['javax.lang.model.type.UnionType', ['javax.lang.model.type.TypeMirror']],
|
|
168
|
+
['javax.lang.model.type.WildcardType', ['javax.lang.model.type.TypeMirror']],
|
|
169
|
+
// JDK collections (fix #391): their declared supertypes, so a
|
|
170
|
+
// `new LinkedHashMap<>()` argument proves a Map parameter applicable.
|
|
171
|
+
...[
|
|
172
|
+
['ArrayList', ['List', 'Collection', 'Iterable', 'RandomAccess', 'AbstractList',
|
|
173
|
+
'AbstractCollection', 'SequencedCollection', 'Cloneable', 'Serializable']],
|
|
174
|
+
['LinkedList', ['List', 'Deque', 'Queue', 'Collection', 'Iterable', 'AbstractSequentialList',
|
|
175
|
+
'AbstractList', 'AbstractCollection', 'SequencedCollection', 'Cloneable', 'Serializable']],
|
|
176
|
+
['ArrayDeque', ['Deque', 'Queue', 'Collection', 'Iterable', 'AbstractCollection',
|
|
177
|
+
'SequencedCollection', 'Cloneable', 'Serializable']],
|
|
178
|
+
// The collection interfaces themselves (fix #428).
|
|
179
|
+
['List', ['SequencedCollection', 'Collection', 'Iterable']],
|
|
180
|
+
['Set', ['Collection', 'Iterable']],
|
|
181
|
+
['Collection', ['Iterable']],
|
|
182
|
+
['Map', []],
|
|
183
|
+
['HashMap', ['Map', 'AbstractMap', 'Cloneable', 'Serializable']],
|
|
184
|
+
['LinkedHashMap', ['HashMap', 'Map', 'SequencedMap', 'AbstractMap', 'Cloneable', 'Serializable']],
|
|
185
|
+
['TreeMap', ['NavigableMap', 'SortedMap', 'SequencedMap', 'Map', 'AbstractMap', 'Cloneable',
|
|
186
|
+
'Serializable']],
|
|
187
|
+
['HashSet', ['Set', 'Collection', 'Iterable', 'AbstractSet', 'AbstractCollection', 'Cloneable',
|
|
188
|
+
'Serializable']],
|
|
189
|
+
['LinkedHashSet', ['HashSet', 'SequencedSet', 'SequencedCollection', 'Set', 'Collection', 'Iterable',
|
|
190
|
+
'AbstractSet', 'AbstractCollection', 'Cloneable', 'Serializable']],
|
|
191
|
+
['TreeSet', ['NavigableSet', 'SortedSet', 'SequencedSet', 'SequencedCollection', 'Set', 'Collection',
|
|
192
|
+
'Iterable', 'AbstractSet', 'AbstractCollection', 'Cloneable', 'Serializable']],
|
|
193
|
+
].map(([type, supertypes]) => [`java.util.${type}`, supertypes.map(name =>
|
|
194
|
+
(name === 'Iterable' || name === 'Cloneable' ? `java.lang.${name}`
|
|
195
|
+
: name === 'Serializable' ? 'java.io.Serializable' : `java.util.${name}`))]),
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
const JAVA_MODELED_PLATFORM_TYPES = new Set([
|
|
199
|
+
...JAVA_PLATFORM_ANCESTOR_MEMBERS.keys(),
|
|
200
|
+
...JAVA_PLATFORM_METHOD_RETURNS.keys()].map(name => name.split('#')[0]));
|
|
201
|
+
for (const type of JAVA_PLATFORM_METHOD_RETURNS.values()) JAVA_MODELED_PLATFORM_TYPES.add(type.replace(/\[\]$/, ''));
|
|
202
|
+
for (const [type, parents] of JAVA_PLATFORM_SUPERTYPES) {
|
|
203
|
+
JAVA_MODELED_PLATFORM_TYPES.add(type);
|
|
204
|
+
for (const parent of parents) JAVA_MODELED_PLATFORM_TYPES.add(parent);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function platformAssignable(actualType, expectedSimple) {
|
|
208
|
+
if (!actualType?.includes('.')) return null;
|
|
209
|
+
const actualSimple = actualType.split('.').pop();
|
|
210
|
+
if (actualSimple === expectedSimple) return true;
|
|
211
|
+
const known = JAVA_PLATFORM_SUPERTYPES.get(actualType);
|
|
212
|
+
if (known) return known.some(type => type.split('.').pop() === expectedSimple);
|
|
213
|
+
if (actualType === 'java.lang.Class') {
|
|
214
|
+
return ['Type', 'Serializable', 'GenericDeclaration', 'AnnotatedElement',
|
|
215
|
+
'Object'].includes(expectedSimple);
|
|
216
|
+
}
|
|
217
|
+
// An unmodeled actual type may implement any of these interfaces.
|
|
218
|
+
// Knowing only the expected type cannot disprove the conversion.
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
JAVA_FINAL_REFERENCE_TYPES, JAVA_PLATFORM_VALUE_ANCESTRY, JAVA_WRAPPER_PRIMITIVES, JAVA_PLATFORM_FIELD_TYPES,
|
|
224
|
+
JAVA_PLATFORM_METHOD_RETURNS, JAVA_PLATFORM_ANCESTOR_MEMBERS, JAVA_PLATFORM_SUPERTYPES,
|
|
225
|
+
JAVA_MODELED_PLATFORM_TYPES, platformAssignable,
|
|
226
|
+
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Modules read lazily (load cycles), each resolved once: a require() per
|
|
4
|
+
// call in hot paths was measurable (fix #436).
|
|
5
|
+
let lazySourceMemoModuleCache = null;
|
|
6
|
+
const lazySourceMemoModule = () => lazySourceMemoModuleCache || (lazySourceMemoModuleCache = require('../core/source-memo'));
|
|
7
|
+
|
|
8
|
+
// Java translates eligible Unicode escapes before tokenization (JLS 3.3).
|
|
9
|
+
// Keep this lexical step separate from name lookup and never rescan output.
|
|
10
|
+
function translateUnicodeEscapes(source) {
|
|
11
|
+
if (!source.includes('\\u')) return source;
|
|
12
|
+
let result = '', backslashes = 0, escaped = false;
|
|
13
|
+
for (let i = 0; i < source.length;) {
|
|
14
|
+
let character = source[i++];
|
|
15
|
+
if (character === '\\' && (escaped || backslashes % 2 === 0) && source[i] === 'u') {
|
|
16
|
+
while (source[i] === 'u') i++;
|
|
17
|
+
let value = 0;
|
|
18
|
+
for (let digit = 0; digit < 4; digit++) {
|
|
19
|
+
const code = source.charCodeAt(i++);
|
|
20
|
+
const hex = code >= 48 && code <= 57 ? code - 48
|
|
21
|
+
: code >= 65 && code <= 70 ? code - 55
|
|
22
|
+
: code >= 97 && code <= 102 ? code - 87 : -1;
|
|
23
|
+
if (hex < 0) return null;
|
|
24
|
+
value = value * 16 + hex;
|
|
25
|
+
}
|
|
26
|
+
character = String.fromCharCode(value);
|
|
27
|
+
escaped = true;
|
|
28
|
+
} else escaped = false;
|
|
29
|
+
result += character;
|
|
30
|
+
backslashes = character === '\\' ? backslashes + 1 : 0;
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Identifier equality ignores these characters (JLS 3.8). An unassigned
|
|
36
|
+
// character may belong to a newer compiler's Unicode table; abstain there.
|
|
37
|
+
const NONCANONICAL_IDENTIFIER = /[\p{Cf}\p{Cn}]/u;
|
|
38
|
+
const LITERAL_PARTS = new Set(['string_fragment', 'multiline_string_fragment', 'escape_sequence']);
|
|
39
|
+
|
|
40
|
+
function noncanonicalIdentifier(text) {
|
|
41
|
+
if (NONCANONICAL_IDENTIFIER.test(text)) return true;
|
|
42
|
+
for (const character of text) {
|
|
43
|
+
const code = character.charCodeAt(0);
|
|
44
|
+
if (code <= 8 || code >= 14 && code <= 27 || code >= 127 && code <= 159) return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function typeNameStructure(tree) {
|
|
50
|
+
if (!tree || tree.rootNode.hasError) return null;
|
|
51
|
+
const tokens = [], stack = [tree.rootNode];
|
|
52
|
+
while (stack.length) {
|
|
53
|
+
const node = stack.pop();
|
|
54
|
+
if (node.isMissing) return null;
|
|
55
|
+
if (node.type === 'line_comment' || node.type === 'block_comment') continue;
|
|
56
|
+
tokens.push(node.type);
|
|
57
|
+
if (node.type === 'string_literal' || node.type === 'character_literal') {
|
|
58
|
+
if (node.namedChildren.some(child => !LITERAL_PARTS.has(child.type))) return null;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const children = node.children;
|
|
62
|
+
if (!children.length) {
|
|
63
|
+
if (noncanonicalIdentifier(node.text)) return null;
|
|
64
|
+
tokens.push(node.text);
|
|
65
|
+
} else {
|
|
66
|
+
tokens.push(children.length);
|
|
67
|
+
for (let i = children.length - 1; i >= 0; i--) stack.push(children[i]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return JSON.stringify(tokens);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Verdicts by source text (the text is the key; see core/source-memo.js).
|
|
74
|
+
const canonicalSources = new (require('../core/source-memo').BoundedMap)({ maxEntries: 1024,
|
|
75
|
+
maxWeight: 32 * 1024 * 1024, weigh: (_verdict, source) => source.length * 2 });
|
|
76
|
+
|
|
77
|
+
function parseNameSource(parser, source, safeParse) {
|
|
78
|
+
// A NUL is legal literal/comment content, but the grammar cannot read it.
|
|
79
|
+
// Project it to an ordinary character, then prove every projected position
|
|
80
|
+
// lies inside that AST content. A NUL in a name or other code stays opaque.
|
|
81
|
+
const positions = [];
|
|
82
|
+
for (let i = source.indexOf('\0'); i !== -1; i = source.indexOf('\0', i + 1)) positions.push(i);
|
|
83
|
+
const tree = safeParse(parser, positions.length ? source.replaceAll('\0', 'x') : source);
|
|
84
|
+
if (!tree) return null;
|
|
85
|
+
for (const position of positions) {
|
|
86
|
+
let node = tree.rootNode.descendantForIndex(position, position + 1);
|
|
87
|
+
while (node && !['string_literal', 'character_literal', 'line_comment', 'block_comment'].includes(node.type)) {
|
|
88
|
+
node = node.parent;
|
|
89
|
+
}
|
|
90
|
+
const contentStart = node?.type.endsWith('comment') ? node.startIndex + 2 : node?.startIndex + 1;
|
|
91
|
+
const contentEnd = node?.type === 'line_comment' ? node.endIndex
|
|
92
|
+
: node?.type === 'block_comment' ? node.endIndex - 2 : node?.endIndex - 1;
|
|
93
|
+
if (!node || position < contentStart || position >= contentEnd) {
|
|
94
|
+
tree.delete?.();
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return tree;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// typeNameStructure(tree) !== null for a source with no character a
|
|
102
|
+
// noncanonical identifier test could reject: an error-free tree (MISSING
|
|
103
|
+
// nodes count as errors) whose literals hold only literal parts. The full
|
|
104
|
+
// token walk serves the other sources (fix #418: it walked every node of
|
|
105
|
+
// every Java file an overload check read).
|
|
106
|
+
function plainStructure(tree, source) {
|
|
107
|
+
if (!tree || tree.rootNode.hasError) return false;
|
|
108
|
+
if (noncanonicalIdentifier(source)) return typeNameStructure(tree) !== null;
|
|
109
|
+
return tree.rootNode.descendantsOfType(['string_literal', 'character_literal'])
|
|
110
|
+
.every(node => node.namedChildren.every(child => LITERAL_PARTS.has(child.type)));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Equal code between corresponding literal/comment regions has the same
|
|
114
|
+
// token stream, including extra comment nodes. Java's grammar does not read
|
|
115
|
+
// their contents as code. Check both trees and literal children so an escape
|
|
116
|
+
// that ends a region or introduces a template cannot use this proof.
|
|
117
|
+
function literalOnlyTranslation(rawTree, translatedTree, source, translated) {
|
|
118
|
+
if (!rawTree || !translatedTree || rawTree.rootNode.hasError || translatedTree.rootNode.hasError) return false;
|
|
119
|
+
const kinds = ['string_literal', 'character_literal', 'line_comment', 'block_comment'];
|
|
120
|
+
const before = rawTree.rootNode.descendantsOfType(kinds);
|
|
121
|
+
const after = translatedTree.rootNode.descendantsOfType(kinds);
|
|
122
|
+
if (before.length !== after.length) return false;
|
|
123
|
+
let rawEnd = 0, translatedEnd = 0;
|
|
124
|
+
for (let i = 0; i < before.length; i++) {
|
|
125
|
+
const left = before[i], right = after[i];
|
|
126
|
+
if (left.type !== right.type || left.startIndex < rawEnd || right.startIndex < translatedEnd) return false;
|
|
127
|
+
const outside = source.slice(rawEnd, left.startIndex);
|
|
128
|
+
if (outside !== translated.slice(translatedEnd, right.startIndex) || noncanonicalIdentifier(outside)) return false;
|
|
129
|
+
if (left.type === 'string_literal' || left.type === 'character_literal') {
|
|
130
|
+
if (left.namedChildren.some(child => !LITERAL_PARTS.has(child.type)) ||
|
|
131
|
+
right.namedChildren.some(child => !LITERAL_PARTS.has(child.type))) return false;
|
|
132
|
+
}
|
|
133
|
+
rawEnd = left.endIndex;
|
|
134
|
+
translatedEnd = right.endIndex;
|
|
135
|
+
}
|
|
136
|
+
const outside = source.slice(rawEnd);
|
|
137
|
+
return outside === translated.slice(translatedEnd) && !noncanonicalIdentifier(outside);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// The index retains raw-source positions. Its type/name facts are usable
|
|
141
|
+
// when lexical translation preserves the AST's names and structure. Literal
|
|
142
|
+
// contents and comment text do not change Java's static argument types.
|
|
143
|
+
// `parseError` (a value or a function giving it) is whether the index's
|
|
144
|
+
// parse of this same content had errors, when known (fix #418): an
|
|
145
|
+
// error-free source with no escape, no string template and no character an
|
|
146
|
+
// identifier test could reject is canonical without a second parse; a source
|
|
147
|
+
// with errors never is.
|
|
148
|
+
function typeNameSourceCanonical(source, parseError = undefined, lines = null) {
|
|
149
|
+
if (typeof source !== 'string') return false;
|
|
150
|
+
if (wholeSourceCanonical(source, parseError)) return true;
|
|
151
|
+
if (!lines) return false;
|
|
152
|
+
// A parse error confined to member declarations leaves the names of
|
|
153
|
+
// every other member as the index read them (fix #421): one unrelated
|
|
154
|
+
// syntax error demoted every overload choice of its file.
|
|
155
|
+
const scopes = errorMemberScopes(source);
|
|
156
|
+
return !!scopes && !scopes.some(([start, end]) => start <= lines.endLine && lines.startLine <= end);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function wholeSourceCanonical(source, parseError) {
|
|
160
|
+
const saved = canonicalSources.get(source);
|
|
161
|
+
if (saved !== undefined) return saved;
|
|
162
|
+
const indexed = typeof parseError === 'function' ? parseError() : parseError;
|
|
163
|
+
let canonical = false;
|
|
164
|
+
if (indexed === true) canonical = false;
|
|
165
|
+
else if (indexed === false && !source.includes('\\u') && !source.includes('\\{') &&
|
|
166
|
+
!noncanonicalIdentifier(source)) canonical = true;
|
|
167
|
+
else {
|
|
168
|
+
const translated = translateUnicodeEscapes(source);
|
|
169
|
+
if (translated !== null) {
|
|
170
|
+
const { getParser, safeParse } = require('./index');
|
|
171
|
+
const parser = getParser('java');
|
|
172
|
+
const rawTree = safeParse(parser, source);
|
|
173
|
+
if (translated === source) canonical = plainStructure(rawTree, source);
|
|
174
|
+
else if (rawTree && !rawTree.rootNode.hasError) {
|
|
175
|
+
const translatedTree = parseNameSource(parser, translated, safeParse);
|
|
176
|
+
canonical = literalOnlyTranslation(rawTree, translatedTree, source, translated);
|
|
177
|
+
if (!canonical) {
|
|
178
|
+
const before = typeNameStructure(rawTree);
|
|
179
|
+
if (before !== null) canonical = before === typeNameStructure(translatedTree);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
canonicalSources.set(source, canonical);
|
|
185
|
+
return canonical;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The member declarations a source's parse errors lie in, by line range
|
|
189
|
+
// ([startLine, endLine] each), or null when an error is outside every member
|
|
190
|
+
// (an import, a type header, a type body between members) or the source
|
|
191
|
+
// needs lexical translation. Keyed by source text.
|
|
192
|
+
const MEMBER_DECLARATIONS = new Set(['method_declaration', 'constructor_declaration',
|
|
193
|
+
'compact_constructor_declaration', 'field_declaration', 'constant_declaration', 'static_initializer',
|
|
194
|
+
'block', 'annotation_type_element_declaration']);
|
|
195
|
+
const TYPE_BODIES = new Set(['class_body', 'interface_body', 'enum_body_declarations', 'annotation_type_body']);
|
|
196
|
+
const errorScopeSources = new (require('../core/source-memo').BoundedMap)({ maxEntries: 256,
|
|
197
|
+
maxWeight: 8 * 1024 * 1024, weigh: (_scopes, source) => source.length * 2 });
|
|
198
|
+
|
|
199
|
+
function errorMemberScopes(source) {
|
|
200
|
+
const saved = errorScopeSources.get(source);
|
|
201
|
+
if (saved !== undefined) return saved;
|
|
202
|
+
let scopes = null;
|
|
203
|
+
if (!source.includes('\\u') && !source.includes('\\{') && !noncanonicalIdentifier(source)) {
|
|
204
|
+
const tree = lazySourceMemoModule().parsedSource('java', source);
|
|
205
|
+
if (tree) {
|
|
206
|
+
scopes = [];
|
|
207
|
+
const stack = [tree.rootNode];
|
|
208
|
+
while (stack.length && scopes) {
|
|
209
|
+
const node = stack.pop();
|
|
210
|
+
if (node.type === 'ERROR' || node.isMissing) {
|
|
211
|
+
let member = node.parent;
|
|
212
|
+
while (member && !(MEMBER_DECLARATIONS.has(member.type) && TYPE_BODIES.has(member.parent?.type))) {
|
|
213
|
+
member = member.type === 'ERROR' ? null : member.parent;
|
|
214
|
+
}
|
|
215
|
+
if (!member) scopes = null;
|
|
216
|
+
else scopes.push([member.startPosition.row + 1, member.endPosition.row + 1]);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (!node.hasError) continue;
|
|
220
|
+
const children = node.children;
|
|
221
|
+
for (let i = children.length - 1; i >= 0; i--) stack.push(children[i]);
|
|
222
|
+
}
|
|
223
|
+
if (scopes && !scopes.length && !plainStructure(tree, source)) scopes = null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
errorScopeSources.set(source, scopes);
|
|
227
|
+
return scopes;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const typeOwnerSources = new (require('../core/source-memo').SourceMemo)({
|
|
231
|
+
maxEntries: 128, maxWeight: 16 * 1024 * 1024,
|
|
232
|
+
weigh: facts => facts.weight,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
function typeNameOwnersAt(source, site) {
|
|
236
|
+
if (typeof source !== 'string' || !Number.isInteger(site?.start) || !Number.isInteger(site?.line)) return null;
|
|
237
|
+
const key = `${site.start}:${site.line}`;
|
|
238
|
+
const facts = typeOwnerSources.get('java', source) || { sites: new Map(), weight: 0 };
|
|
239
|
+
const { sites } = facts;
|
|
240
|
+
if (sites.has(key)) return sites.get(key);
|
|
241
|
+
const owners = typeNameOwnersAtUncached(source, site);
|
|
242
|
+
const result = owners && Object.freeze(owners.map(owner => Object.freeze(owner)));
|
|
243
|
+
if (sites.size < 4096) {
|
|
244
|
+
sites.set(key, result);
|
|
245
|
+
facts.weight += key.length * 2 + 32 + (result?.length || 0) * 32;
|
|
246
|
+
typeOwnerSources.set('java', source, facts);
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function typeNameOwnersAtUncached(source, site) {
|
|
252
|
+
if (!typeNameSourceCanonical(source) || !Number.isInteger(site?.start)) return null;
|
|
253
|
+
const tree = lazySourceMemoModule().parsedSource('java', source);
|
|
254
|
+
if (!tree) return null;
|
|
255
|
+
let node = tree.rootNode.descendantForIndex(site.start, site.start + 1);
|
|
256
|
+
if (!node || node.startPosition.row + 1 !== site.line) return null;
|
|
257
|
+
const owners = [];
|
|
258
|
+
for (; node; node = node.parent) {
|
|
259
|
+
// An anonymous body can inherit type names from its own base.
|
|
260
|
+
if (node.type === 'class_body' && ['object_creation_expression', 'enum_constant'].includes(node.parent?.type)) return null;
|
|
261
|
+
if (['class_declaration', 'interface_declaration', 'enum_declaration',
|
|
262
|
+
'record_declaration', 'annotation_type_declaration'].includes(node.type)) {
|
|
263
|
+
owners.push({ start: node.startIndex, end: node.endIndex });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return owners.length ? owners : null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module.exports = { translateUnicodeEscapes, typeNameSourceCanonical, typeNameOwnersAt };
|