Skip to content

Commit 637943e

Browse files
authored
feat(jdk-codemodel): source fidelity - locations, imports, nested types, initializer blocks, varargs, class literals (#56)
1 parent ff1f774 commit 637943e

30 files changed

Lines changed: 1384 additions & 23 deletions

jdk-codemodel/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105
<scope>test</scope>
106106
</dependency>
107107

108+
<dependency>
109+
<groupId>build.base</groupId>
110+
<artifactId>base-telemetry</artifactId>
111+
</dependency>
112+
108113
<dependency>
109114
<groupId>build.base</groupId>
110115
<artifactId>base-telemetry-foundation</artifactId>

jdk-codemodel/src/main/java/build/codemodel/jdk/JDKCodeModel.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,11 @@ public Optional<JDKTypeDescriptor> getJDKTypeDescriptor(final TypeName typeName)
637637
return Optional.empty();
638638
}
639639

640+
final var existing = getTypeDescriptor(typeName);
641+
if (existing.isPresent()) {
642+
return existing.map(JDKTypeDescriptor.class::cast);
643+
}
644+
640645
try {
641646
final var typeUsageClass = Thread.currentThread().getContextClassLoader()
642647
.loadClass(typeName.binaryName());

jdk-codemodel/src/main/java/build/codemodel/jdk/JdkExpressionConverter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import build.codemodel.jdk.expression.BitwiseBinary;
5353
import build.codemodel.jdk.expression.BitwiseOperator;
5454
import build.codemodel.jdk.expression.CharLiteral;
55+
import build.codemodel.jdk.expression.ClassLiteral;
5556
import build.codemodel.jdk.expression.CompoundAssignment;
5657
import build.codemodel.jdk.expression.FieldAccess;
5758
import build.codemodel.jdk.expression.Identifier;
@@ -328,7 +329,7 @@ public Expression visitMethodInvocation(final MethodInvocationTree t, final Void
328329
final var receiverExpr = ms.getExpression();
329330
target = convert(receiverExpr);
330331
methodName = ms.getIdentifier().toString();
331-
receiverType = resolveReceiverType(receiverExpr);
332+
receiverType = resolveReceiverType(receiverExpr).or(target::type);
332333
} else {
333334
target = null;
334335
methodName = t.getMethodSelect().toString();
@@ -382,6 +383,9 @@ private Optional<TypeMirror> resolveTypeMirror(final Tree tree) {
382383

383384
@Override
384385
public Expression visitMemberSelect(final MemberSelectTree t, final Void v) {
386+
if ("class".equals(t.getIdentifier().toString())) {
387+
return ClassLiteral.of(codeModel, resolveTypeUsage(t.getExpression()));
388+
}
385389
final var receiverExpr = t.getExpression();
386390
return FieldAccess.of(
387391
convert(receiverExpr),

jdk-codemodel/src/main/java/build/codemodel/jdk/JdkInitializer.java

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,20 @@
4949
import build.codemodel.jdk.descriptor.EnumType;
5050
import build.codemodel.jdk.descriptor.FieldInitializerDescriptor;
5151
import build.codemodel.jdk.descriptor.Final;
52+
import build.codemodel.jdk.descriptor.ImportDeclaration;
53+
import build.codemodel.jdk.descriptor.InitializerBlockDescriptor;
5254
import build.codemodel.jdk.descriptor.JDKClassTypeDescriptor;
5355
import build.codemodel.jdk.descriptor.JDKInterfaceTypeDescriptor;
5456
import build.codemodel.jdk.descriptor.JDKModuleDescriptor;
5557
import build.codemodel.jdk.descriptor.JDKTypeDescriptor;
58+
import build.codemodel.jdk.descriptor.LocationTrait;
59+
import build.codemodel.jdk.descriptor.MemberTypeDescriptor;
5660
import build.codemodel.jdk.descriptor.MethodBodyDescriptor;
5761
import build.codemodel.jdk.descriptor.MethodImplementationDescriptor;
5862
import build.codemodel.jdk.descriptor.RecordComponentDescriptor;
5963
import build.codemodel.jdk.descriptor.RecordType;
6064
import build.codemodel.jdk.descriptor.Static;
65+
import build.codemodel.jdk.descriptor.Varargs;
6166
import build.codemodel.objectoriented.descriptor.AccessModifier;
6267
import build.codemodel.objectoriented.descriptor.Classification;
6368
import build.codemodel.objectoriented.descriptor.ConstructorDescriptor;
@@ -67,6 +72,7 @@
6772
import build.codemodel.objectoriented.descriptor.MethodDescriptor;
6873
import build.codemodel.objectoriented.descriptor.ParameterizedTypeDescriptor;
6974
import build.codemodel.objectoriented.naming.MethodName;
75+
import com.sun.source.tree.BlockTree;
7076
import com.sun.source.tree.ClassTree;
7177
import com.sun.source.tree.CompilationUnitTree;
7278
import com.sun.source.tree.MethodTree;
@@ -90,6 +96,7 @@
9096
import java.util.Objects;
9197
import java.util.Optional;
9298
import java.util.stream.Collectors;
99+
import java.util.stream.IntStream;
93100
import java.util.stream.Stream;
94101
import javax.lang.model.element.AnnotationMirror;
95102
import javax.lang.model.element.Element;
@@ -340,10 +347,23 @@ private void processTypeElement(final TypeElement typeElement,
340347
typeDescriptor.addTrait(new EnclosingTypeDescriptor(resolveTypeName(enclosingElement)));
341348
}
342349

350+
if (classTree != null && cut != null) {
351+
final var srcPositions = trees.getSourcePositions();
352+
final var start = srcPositions.getStartPosition(cut, classTree);
353+
final var end = srcPositions.getEndPosition(cut, classTree);
354+
if (start != Diagnostic.NOPOS) {
355+
typeDescriptor.addTrait(LocationTrait.of(cut.getSourceFile().toUri(), start, end));
356+
}
357+
}
358+
343359
addTypeParameters(typeDescriptor, typeElement);
344360
addModifiers(typeDescriptor, typeElement);
345361
addSuperclass(typeDescriptor, typeElement);
346362
addInterfaces(typeDescriptor, typeElement);
363+
addMemberTypes(typeDescriptor, typeElement);
364+
if (!(typeElement.getEnclosingElement() instanceof TypeElement)) {
365+
addImports(typeDescriptor, cut);
366+
}
347367
processMembers(typeDescriptor, typeElement, classTree, cut);
348368
if (typeElement.getKind() == ElementKind.RECORD) {
349369
addRecordComponents(typeDescriptor, typeElement);
@@ -415,6 +435,36 @@ private void addInterfaces(final JDKTypeDescriptor typeDescriptor, final TypeEle
415435
}
416436
}
417437

438+
private void addMemberTypes(final JDKTypeDescriptor typeDescriptor, final TypeElement typeElement) {
439+
typeElement.getEnclosedElements().stream()
440+
.filter(e -> e.getKind().isClass() || e.getKind().isInterface())
441+
.map(TypeElement.class::cast)
442+
.forEach(nested -> typeDescriptor.addTrait(new MemberTypeDescriptor(resolveTypeName(nested))));
443+
}
444+
445+
private void addImports(final JDKTypeDescriptor typeDescriptor, final CompilationUnitTree cut) {
446+
if (cut == null) {
447+
return;
448+
}
449+
final var imports = cut.getImports();
450+
for (int i = 0; i < imports.size(); i++) {
451+
final var importTree = imports.get(i);
452+
final var qualifiedId = importTree.getQualifiedIdentifier().toString();
453+
final boolean isStatic = importTree.isStatic();
454+
final boolean isOnDemand = qualifiedId.endsWith(".*");
455+
final var name = isOnDemand ? qualifiedId.substring(0, qualifiedId.length() - 2) : qualifiedId;
456+
if (isStatic && isOnDemand) {
457+
typeDescriptor.addTrait(ImportDeclaration.ofStaticOnDemand(name, i));
458+
} else if (isStatic) {
459+
typeDescriptor.addTrait(ImportDeclaration.ofStatic(name, i));
460+
} else if (isOnDemand) {
461+
typeDescriptor.addTrait(ImportDeclaration.ofOnDemand(name, i));
462+
} else {
463+
typeDescriptor.addTrait(ImportDeclaration.of(name, i));
464+
}
465+
}
466+
}
467+
418468
private void processMembers(final JDKTypeDescriptor typeDescriptor,
419469
final TypeElement typeElement,
420470
final ClassTree classTree,
@@ -426,6 +476,7 @@ private void processMembers(final JDKTypeDescriptor typeDescriptor,
426476
final var sortedMembers = classTree.getMembers().stream()
427477
.sorted(java.util.Comparator.comparingLong(m -> srcPositions.getStartPosition(cut, m)))
428478
.toList();
479+
int enumConstantOrder = 0;
429480
for (final var member : sortedMembers) {
430481
final var path = TreePath.getPath(cut, member);
431482
if (path == null) {
@@ -434,25 +485,29 @@ private void processMembers(final JDKTypeDescriptor typeDescriptor,
434485
final var elem = trees.getElement(path);
435486
if (member instanceof VariableTree vt && elem instanceof VariableElement ve) {
436487
if (ve.getKind() == ElementKind.FIELD) {
437-
processField(typeDescriptor, ve, vt, cut);
488+
processField(typeDescriptor, ve, vt, cut, srcPositions);
438489
} else if (ve.getKind() == ElementKind.ENUM_CONSTANT) {
439490
final var name = nameProvider.getIrreducibleName(ve.getSimpleName());
440-
typeDescriptor.addTrait(EnumConstantDescriptor.of(name));
491+
typeDescriptor.addTrait(EnumConstantDescriptor.of(name, enumConstantOrder++));
441492
}
442493
} else if (member instanceof MethodTree mt && elem instanceof ExecutableElement ee) {
443494
if (ee.getKind() == ElementKind.CONSTRUCTOR) {
444495
processConstructor(typeDescriptor, ee, mt, cut, srcPositions);
445496
} else if (ee.getKind() == ElementKind.METHOD) {
446-
processMethod(typeDescriptor, ee, mt, cut);
497+
processMethod(typeDescriptor, ee, mt, cut, srcPositions);
447498
}
499+
} else if (member instanceof BlockTree bt) {
500+
typeDescriptor.addTrait(
501+
new InitializerBlockDescriptor(bt.isStatic(), stmtConverter.convertStatements(bt.getStatements())));
448502
}
449503
}
450504
}
451505

452506
private void processField(final JDKTypeDescriptor typeDescriptor,
453507
final VariableElement fieldElement,
454508
final VariableTree varTree,
455-
final CompilationUnitTree cut) {
509+
final CompilationUnitTree cut,
510+
final SourcePositions srcPositions) {
456511
final var fieldName = nameProvider.getIrreducibleName(fieldElement.getSimpleName());
457512
final var fieldType = resolveTypeUsage(fieldElement.asType(), fieldElement);
458513
final var fieldDescriptor = FieldDescriptor.of(codeModel, fieldName, fieldType);
@@ -466,6 +521,12 @@ private void processField(final JDKTypeDescriptor typeDescriptor,
466521
.map(mirror -> createAnnotationTypeUsage(fieldElement, mirror))
467522
.forEach(fieldDescriptor::addTrait);
468523

524+
final var start = srcPositions.getStartPosition(cut, varTree);
525+
final var end = srcPositions.getEndPosition(cut, varTree);
526+
if (start != Diagnostic.NOPOS) {
527+
fieldDescriptor.addTrait(LocationTrait.of(cut.getSourceFile().toUri(), start, end));
528+
}
529+
469530
typeDescriptor.addTrait(fieldDescriptor);
470531

471532
if (varTree.getInitializer() != null) {
@@ -496,6 +557,12 @@ private void processConstructor(final JDKTypeDescriptor typeDescriptor,
496557
.map(mirror -> createAnnotationTypeUsage(methodElement, mirror))
497558
.forEach(constructorDescriptor::addTrait);
498559

560+
final var start = srcPositions.getStartPosition(cut, ctorTree);
561+
final var end = srcPositions.getEndPosition(cut, ctorTree);
562+
if (start != Diagnostic.NOPOS) {
563+
constructorDescriptor.addTrait(LocationTrait.of(cut.getSourceFile().toUri(), start, end));
564+
}
565+
499566
typeDescriptor.addTrait(constructorDescriptor);
500567

501568
if (ctorTree.getBody() != null) {
@@ -512,15 +579,19 @@ private void processConstructor(final JDKTypeDescriptor typeDescriptor,
512579

513580
private Stream<FormalParameterDescriptor> getFormalParameters(final ExecutableElement methodElement) {
514581
final var params = methodElement.getParameters();
515-
return params.stream().map(variableElement -> {
516-
final var param = variableElement;
582+
final int lastIdx = params.size() - 1;
583+
return IntStream.range(0, params.size()).mapToObj(i -> {
584+
final var param = params.get(i);
517585
final var pd = FormalParameterDescriptor.of(
518586
codeModel,
519587
Optional.of(nameProvider.getIrreducibleName(param.getSimpleName())),
520588
resolveTypeUsage(param.asType(), param));
521589
if (param.getModifiers().contains(Modifier.FINAL)) {
522590
pd.addTrait(Final.FINAL);
523591
}
592+
if (methodElement.isVarArgs() && i == lastIdx) {
593+
pd.addTrait(Varargs.VARARGS);
594+
}
524595
param.getAnnotationMirrors().stream()
525596
.map(mirror -> createAnnotationTypeUsage(param, mirror))
526597
.forEach(pd::addTrait);
@@ -531,7 +602,8 @@ private Stream<FormalParameterDescriptor> getFormalParameters(final ExecutableEl
531602
private void processMethod(final JDKTypeDescriptor typeDescriptor,
532603
final ExecutableElement methodElement,
533604
final MethodTree methodTree,
534-
final CompilationUnitTree cut) {
605+
final CompilationUnitTree cut,
606+
final SourcePositions srcPositions) {
535607
final var methodSimpleName = nameProvider.getIrreducibleName(methodElement.getSimpleName());
536608
final var returnType = resolveTypeUsage(methodElement.getReturnType(), methodElement);
537609
final var methodName = MethodName.of(
@@ -572,6 +644,12 @@ private void processMethod(final JDKTypeDescriptor typeDescriptor,
572644
.map(mirror -> createAnnotationTypeUsage(methodElement, mirror))
573645
.forEach(methodDescriptor::addTrait);
574646

647+
final var start = srcPositions.getStartPosition(cut, methodTree);
648+
final var end = srcPositions.getEndPosition(cut, methodTree);
649+
if (start != Diagnostic.NOPOS) {
650+
methodDescriptor.addTrait(LocationTrait.of(cut.getSourceFile().toUri(), start, end));
651+
}
652+
575653
typeDescriptor.addTrait(methodDescriptor);
576654

577655
if (methodTree.getBody() != null) {

jdk-codemodel/src/main/java/build/codemodel/jdk/descriptor/EnumConstantDescriptor.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ public final class EnumConstantDescriptor implements Trait {
3737
* The {@link IrreducibleName} of the enum constant.
3838
*/
3939
private final IrreducibleName name;
40+
private final int order;
4041

41-
private EnumConstantDescriptor(final IrreducibleName name) {
42+
private EnumConstantDescriptor(final IrreducibleName name, final int order) {
4243
this.name = Objects.requireNonNull(name, "The name must not be null");
44+
this.order = order;
4345
}
4446

4547
/**
@@ -51,13 +53,23 @@ public IrreducibleName name() {
5153
return this.name;
5254
}
5355

56+
/**
57+
* Obtains the zero-based declaration order of this enum constant within its type.
58+
*
59+
* @return the declaration order
60+
*/
61+
public int order() {
62+
return this.order;
63+
}
64+
5465
/**
5566
* Creates an {@link EnumConstantDescriptor}.
5667
*
57-
* @param name the {@link IrreducibleName} of the enum constant
68+
* @param name the {@link IrreducibleName} of the enum constant
69+
* @param order the zero-based declaration order
5870
* @return a new {@link EnumConstantDescriptor}
5971
*/
60-
public static EnumConstantDescriptor of(final IrreducibleName name) {
61-
return new EnumConstantDescriptor(name);
72+
public static EnumConstantDescriptor of(final IrreducibleName name, final int order) {
73+
return new EnumConstantDescriptor(name, order);
6274
}
6375
}

jdk-codemodel/src/main/java/build/codemodel/jdk/descriptor/FieldInitializerDescriptor.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
* #L%
2121
*/
2222

23+
import build.base.foundation.iterator.Iterators;
24+
import build.base.mereology.Composite;
2325
import build.codemodel.expression.Expression;
2426
import build.codemodel.foundation.descriptor.Trait;
2527

28+
import java.util.Iterator;
2629
import java.util.Objects;
2730

2831
/**
@@ -32,7 +35,7 @@
3235
* @since Mar-2026
3336
*/
3437
public final class FieldInitializerDescriptor
35-
implements Trait {
38+
implements Composite, Trait {
3639

3740
private final Expression initializer;
3841

@@ -53,4 +56,11 @@ public FieldInitializerDescriptor(final Expression initializer) {
5356
public Expression initializer() {
5457
return this.initializer;
5558
}
59+
60+
@Override
61+
public <T> Iterator<T> iterator(final Class<T> type) {
62+
return type.isInstance(initializer)
63+
? Iterators.of(type.cast(initializer))
64+
: Iterators.empty();
65+
}
5666
}

0 commit comments

Comments
 (0)