Skip to content

Commit

Permalink
fix: use typemap in JVM (#2556)
Browse files Browse the repository at this point in the history
fixes: #2513
fixes: #2510
  • Loading branch information
stuartwdouglas authored Sep 1, 2024
1 parent 0db00b2 commit 58ce5a0
Show file tree
Hide file tree
Showing 24 changed files with 609 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import xyz.block.ftl.LeaseClient;
import xyz.block.ftl.Secret;
import xyz.block.ftl.Subscription;
import xyz.block.ftl.TypeAlias;
import xyz.block.ftl.TypeAliasMapper;
import xyz.block.ftl.Verb;

public class FTLDotNames {
Expand All @@ -21,6 +23,8 @@ private FTLDotNames() {
public static final DotName EXPORT = DotName.createSimple(Export.class);
public static final DotName VERB = DotName.createSimple(Verb.class);
public static final DotName CRON = DotName.createSimple(Cron.class);
public static final DotName TYPE_ALIAS_MAPPER = DotName.createSimple(TypeAliasMapper.class);
public static final DotName TYPE_ALIAS = DotName.createSimple(TypeAlias.class);
public static final DotName SUBSCRIPTION = DotName.createSimple(Subscription.class);
public static final DotName LEASE_CLIENT = DotName.createSimple(LeaseClient.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import org.eclipse.microprofile.config.Config;
Expand All @@ -24,6 +25,7 @@
public abstract class JVMCodeGenerator implements CodeGenProvider {

public static final String PACKAGE_PREFIX = "ftl.";
public static final String TYPE_MAPPER = "TypeAliasMapper";

@Override
public String providerId() {
Expand All @@ -42,6 +44,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
}
List<Module> modules = new ArrayList<>();
Map<DeclRef, Type> typeAliasMap = new HashMap<>();
Map<DeclRef, String> nativeTypeAliasMap = new HashMap<>();
try (Stream<Path> pathStream = Files.list(context.inputDir())) {
for (var file : pathStream.toList()) {
String fileName = file.getFileName().toString();
Expand All @@ -50,9 +53,30 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
}
var module = Module.parseFrom(Files.readAllBytes(file));
for (var decl : module.getDeclsList()) {
String packageName = PACKAGE_PREFIX + module.getName();
if (decl.hasTypeAlias()) {
var data = decl.getTypeAlias();
typeAliasMap.put(new DeclRef(module.getName(), data.getName()), data.getType());
boolean handled = false;
for (var md : data.getMetadataList()) {
if (md.hasTypeMap()) {
String runtime = md.getTypeMap().getRuntime();
if (runtime.equals("kotlin") || runtime.equals("java")) {
nativeTypeAliasMap.put(new DeclRef(module.getName(), data.getName()),
md.getTypeMap().getNativeName());
generateTypeAliasMapper(module.getName(), data.getName(), packageName,
Optional.of(md.getTypeMap().getNativeName()),
context.outDir());
handled = true;
break;
}
}
}
if (!handled) {
generateTypeAliasMapper(module.getName(), data.getName(), packageName, Optional.empty(),
context.outDir());
typeAliasMap.put(new DeclRef(module.getName(), data.getName()), data.getType());
}

}
}
modules.add(module);
Expand All @@ -69,26 +93,27 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
if (!verb.getExport()) {
continue;
}
generateVerb(module, verb, packageName, typeAliasMap, context.outDir());
generateVerb(module, verb, packageName, typeAliasMap, nativeTypeAliasMap, context.outDir());
} else if (decl.hasData()) {
var data = decl.getData();
if (!data.getExport()) {
continue;
}
generateDataObject(module, data, packageName, typeAliasMap, context.outDir());
generateDataObject(module, data, packageName, typeAliasMap, nativeTypeAliasMap, context.outDir());

} else if (decl.hasEnum()) {
var data = decl.getEnum();
if (!data.getExport()) {
continue;
}
generateEnum(module, data, packageName, typeAliasMap, context.outDir());
generateEnum(module, data, packageName, typeAliasMap, nativeTypeAliasMap, context.outDir());
} else if (decl.hasTopic()) {
var data = decl.getTopic();
if (!data.getExport()) {
continue;
}
generateTopicSubscription(module, data, packageName, typeAliasMap, context.outDir());
generateTopicSubscription(module, data, packageName, typeAliasMap, nativeTypeAliasMap,
context.outDir());
}
}
}
Expand All @@ -99,17 +124,20 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
return true;
}

protected abstract void generateTypeAliasMapper(String module, String name, String packageName,
Optional<String> nativeTypeAlias, Path outputDir) throws IOException;

protected abstract void generateTopicSubscription(Module module, Topic data, String packageName,
Map<DeclRef, Type> typeAliasMap, Path outputDir) throws IOException;
Map<DeclRef, Type> typeAliasMap, Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;

protected abstract void generateEnum(Module module, Enum data, String packageName, Map<DeclRef, Type> typeAliasMap,
Path outputDir) throws IOException;
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;

protected abstract void generateDataObject(Module module, Data data, String packageName, Map<DeclRef, Type> typeAliasMap,
Path outputDir) throws IOException;
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;

protected abstract void generateVerb(Module module, Verb verb, String packageName, Map<DeclRef, Type> typeAliasMap,
Path outputDir) throws IOException;
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;

@Override
public boolean shouldRun(Path sourceDir, Config config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@
import xyz.block.ftl.v1.schema.Metadata;
import xyz.block.ftl.v1.schema.MetadataAlias;
import xyz.block.ftl.v1.schema.MetadataCalls;
import xyz.block.ftl.v1.schema.MetadataTypeMap;
import xyz.block.ftl.v1.schema.Module;
import xyz.block.ftl.v1.schema.Optional;
import xyz.block.ftl.v1.schema.Ref;
import xyz.block.ftl.v1.schema.Time;
import xyz.block.ftl.v1.schema.Type;
import xyz.block.ftl.v1.schema.TypeAlias;
import xyz.block.ftl.v1.schema.Unit;
import xyz.block.ftl.v1.schema.Verb;

Expand All @@ -75,7 +77,7 @@ public class ModuleBuilder {

private final IndexView index;
private final Module.Builder moduleBuilder;
private final Map<TypeKey, ExistingRef> dataElements = new HashMap<>();
private final Map<TypeKey, ExistingRef> dataElements;
private final String moduleName;
private final Set<String> knownSecrets = new HashSet<>();
private final Set<String> knownConfig = new HashSet<>();
Expand All @@ -86,7 +88,7 @@ public class ModuleBuilder {

public ModuleBuilder(IndexView index, String moduleName, Map<DotName, TopicsBuildItem.DiscoveredTopic> knownTopics,
Map<DotName, VerbClientBuildItem.DiscoveredClients> verbClients, FTLRecorder recorder,
Map<String, String> verbDocs) {
Map<String, String> verbDocs, Map<TypeKey, ExistingRef> typeAliases) {
this.index = index;
this.moduleName = moduleName;
this.moduleBuilder = Module.newBuilder()
Expand All @@ -96,6 +98,7 @@ public ModuleBuilder(IndexView index, String moduleName, Map<DotName, TopicsBuil
this.verbClients = verbClients;
this.recorder = recorder;
this.verbDocs = verbDocs;
this.dataElements = new HashMap<>(typeAliases);
}

public static @NotNull String methodToName(MethodInfo method) {
Expand Down Expand Up @@ -435,11 +438,16 @@ public void writeTo(OutputStream out) throws IOException {
moduleBuilder.build().writeTo(out);
}

record ExistingRef(Ref ref, boolean exported) {

public void registerTypeAlias(String name, org.jboss.jandex.Type finalT, org.jboss.jandex.Type finalS, boolean exported) {
moduleBuilder.addDecls(Decl.newBuilder()
.setTypeAlias(TypeAlias.newBuilder().setType(buildType(finalS, exported)).setName(name).addMetadata(Metadata
.newBuilder()
.setTypeMap(MetadataTypeMap.newBuilder().setRuntime("java").setNativeName(finalT.toString()).build())
.build()))
.build());
}

private record TypeKey(String name, List<String> typeParams) {
record ExistingRef(Ref ref, boolean exported) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.stream.Collectors;

import org.jboss.jandex.DotName;
import org.jboss.jandex.ParameterizedType;
import org.jboss.logging.Logger;
import org.tomlj.Toml;
import org.tomlj.TomlParseResult;
Expand Down Expand Up @@ -42,6 +43,7 @@
import xyz.block.ftl.runtime.VerbRegistry;
import xyz.block.ftl.runtime.config.FTLConfigSourceFactoryBuilder;
import xyz.block.ftl.runtime.http.FTLHttpHandler;
import xyz.block.ftl.v1.schema.Ref;

public class ModuleProcessor {

Expand Down Expand Up @@ -108,6 +110,7 @@ public void generateSchema(CombinedIndexBuildItem index,
ModuleNameBuildItem moduleNameBuildItem,
TopicsBuildItem topicsBuildItem,
VerbClientBuildItem verbClientBuildItem,
List<TypeAliasBuildItem> typeAliasBuildItems,
List<SchemaContributorBuildItem> schemaContributorBuildItems) throws Exception {
String moduleName = moduleNameBuildItem.getModuleName();
Map<String, String> verbDocs = new HashMap<>();
Expand All @@ -125,9 +128,25 @@ public void generateSchema(CombinedIndexBuildItem index,
}
}
}
Map<TypeKey, ModuleBuilder.ExistingRef> existingRefs = new HashMap<>();
for (var i : typeAliasBuildItems) {
String mn;
if (i.getModule().isEmpty()) {
mn = moduleNameBuildItem.getModuleName();
} else {
mn = i.getModule();
}
if (i.getLocalType() instanceof ParameterizedType) {
//TODO: we can't handle this yet
// existingRefs.put(new TypeKey(i.getLocalType().name().toString(), i.getLocalType().asParameterizedType().arguments().stream().map(i.)), new ModuleBuilder.ExistingRef(Ref.newBuilder().setModule(moduleName).setName(i.getName()).build(), i.isExported()));
} else {
existingRefs.put(new TypeKey(i.getLocalType().name().toString(), List.of()), new ModuleBuilder.ExistingRef(
Ref.newBuilder().setModule(mn).setName(i.getName()).build(), i.isExported()));
}
}

ModuleBuilder moduleBuilder = new ModuleBuilder(index.getComputingIndex(), moduleName, topicsBuildItem.getTopics(),
verbClientBuildItem.getVerbClients(), recorder, verbDocs);
verbClientBuildItem.getVerbClients(), recorder, verbDocs, existingRefs);

for (var i : schemaContributorBuildItems) {
i.getSchemaContributor().accept(moduleBuilder);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package xyz.block.ftl.deployment;

import org.jboss.jandex.Type;

import io.quarkus.builder.item.MultiBuildItem;

public final class TypeAliasBuildItem extends MultiBuildItem {

final String name;
final String module;
final Type localType;
final Type serializedType;
final boolean exported;

public TypeAliasBuildItem(String name, String module, Type localType, Type serializedType, boolean exported) {
this.name = name;
this.module = module;
this.localType = localType;
this.serializedType = serializedType;
this.exported = exported;
}

public String getName() {
return name;
}

public String getModule() {
return module;
}

public Type getLocalType() {
return localType;
}

public Type getSerializedType() {
return serializedType;
}

public boolean isExported() {
return exported;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package xyz.block.ftl.deployment;

import org.jboss.jandex.Type;
import org.jboss.jandex.TypeVariable;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;

public class TypeAliasProcessor {

@BuildStep
public void processTypeAlias(CombinedIndexBuildItem index,
BuildProducer<SchemaContributorBuildItem> schemaContributorBuildItemBuildProducer,
BuildProducer<AdditionalBeanBuildItem> additionalBeanBuildItem,
BuildProducer<TypeAliasBuildItem> typeAliasBuildItemBuildProducer) {
var beans = new AdditionalBeanBuildItem.Builder().setUnremovable();
for (var mapper : index.getIndex().getAnnotations(FTLDotNames.TYPE_ALIAS)) {
boolean exported = mapper.target().hasAnnotation(FTLDotNames.EXPORT);
// This may or may not be the actual mapper, it may be a subclass

var mapperClass = mapper.target().asClass();
var actualMapper = mapperClass;

Type t = null;
Type s = null;
if (mapperClass.isInterface()) {
for (var i : mapperClass.interfaceTypes()) {
if (i.name().equals(FTLDotNames.TYPE_ALIAS_MAPPER)) {
t = i.asParameterizedType().arguments().get(0);
s = i.asParameterizedType().arguments().get(1);
break;
}
}
var implementations = index.getComputingIndex().getAllKnownImplementors(mapperClass.name());
if (implementations.isEmpty()) {
continue;
}
if (implementations.size() > 1) {
throw new RuntimeException(
"Multiple implementations of " + mapperClass.name() + " found: " + implementations);
}
actualMapper = implementations.iterator().next();
}

//TODO: this is a bit hacky and won't work for complex heirachies
// it is enough to get us going through
for (var i : actualMapper.interfaceTypes()) {
if (i.name().equals(FTLDotNames.TYPE_ALIAS_MAPPER)) {
t = i.asParameterizedType().arguments().get(0);
s = i.asParameterizedType().arguments().get(1);
break;
} else if (i.name().equals(mapperClass.name())) {
if (t instanceof TypeVariable) {
t = i.asParameterizedType().arguments().get(0);
}
if (s instanceof TypeVariable) {
s = i.asParameterizedType().arguments().get(1);
}
break;
}
}

beans.addBeanClass(actualMapper.name().toString());
var finalT = t;
var finalS = s;
String module = mapper.value("module") == null ? "" : mapper.value("module").asString();
String name = mapper.value("name").asString();
typeAliasBuildItemBuildProducer.produce(new TypeAliasBuildItem(name, module, t, s, exported));
if (module.isEmpty()) {
schemaContributorBuildItemBuildProducer.produce(new SchemaContributorBuildItem(moduleBuilder -> moduleBuilder
.registerTypeAlias(name, finalT, finalS, exported)));
}

}
additionalBeanBuildItem.produce(beans.build());

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package xyz.block.ftl.deployment;

import java.util.List;

record TypeKey(String name, List<String> typeParams) {

}
5 changes: 5 additions & 0 deletions jvm-runtime/ftl-runtime/common/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Loading

0 comments on commit 58ce5a0

Please sign in to comment.