Skip to content

Commit 0ec0b0f

Browse files
Add fluss-codegen module for runtime code generation
This commit introduces a new fluss-codegen module that provides runtime code generation capabilities using Janino compiler. Core framework: - CodeGeneratorContext: Manages reusable code fragments and member variables - JavaCodeBuilder: Type-safe builder for constructing Java source code with support for modifiers, parameters, and various code constructs - CompileUtils: Compiles generated source code using Janino with caching - GeneratedClass: Wrapper for generated class with source and compiled class - CodeGenException: Exception for code generation failures Code generators: - EqualiserCodeGenerator: Generates RecordEqualiser implementations for comparing InternalRow instances, supporting all Fluss data types Key features: - Type-safe API with Modifier enum, PrimitiveType enum, and Param class - Comprehensive support for all Fluss data types including nested types - Field projection support for partial row comparison - Compiled class caching for performance - Janino dependency shaded to avoid classpath conflicts Testing: - Comprehensive unit tests for all components - Generated code snapshot tests for reviewer understanding - 297 test cases covering edge cases and error handling License: - Janino (BSD 3-Clause) license included in META-INF/licenses/
1 parent 3c53a20 commit 0ec0b0f

File tree

49 files changed

+5266
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+5266
-0
lines changed

fluss-codegen/pom.xml

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-->
19+
20+
<project xmlns="http://maven.apache.org/POM/4.0.0"
21+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
23+
<modelVersion>4.0.0</modelVersion>
24+
<parent>
25+
<groupId>org.apache.fluss</groupId>
26+
<artifactId>fluss</artifactId>
27+
<version>0.9-SNAPSHOT</version>
28+
</parent>
29+
30+
<artifactId>fluss-codegen</artifactId>
31+
32+
<name>Fluss : Code Gen</name>
33+
<packaging>jar</packaging>
34+
<description>
35+
Code generation module for Fluss, providing runtime code generation
36+
for high-performance record comparison and projection operations.
37+
</description>
38+
39+
<properties>
40+
<janino.version>3.1.9</janino.version>
41+
</properties>
42+
43+
<dependencies>
44+
<dependency>
45+
<groupId>org.apache.fluss</groupId>
46+
<artifactId>fluss-common</artifactId>
47+
<version>${project.version}</version>
48+
</dependency>
49+
50+
<!-- Janino for runtime Java compilation -->
51+
<dependency>
52+
<groupId>org.codehaus.janino</groupId>
53+
<artifactId>janino</artifactId>
54+
<version>${janino.version}</version>
55+
</dependency>
56+
57+
<dependency>
58+
<groupId>org.codehaus.janino</groupId>
59+
<artifactId>commons-compiler</artifactId>
60+
<version>${janino.version}</version>
61+
</dependency>
62+
63+
<!-- Test dependencies -->
64+
<dependency>
65+
<groupId>org.apache.fluss</groupId>
66+
<artifactId>fluss-test-utils</artifactId>
67+
</dependency>
68+
69+
<dependency>
70+
<groupId>org.apache.fluss</groupId>
71+
<artifactId>fluss-common</artifactId>
72+
<version>${project.version}</version>
73+
<type>test-jar</type>
74+
<scope>test</scope>
75+
</dependency>
76+
</dependencies>
77+
78+
<build>
79+
<plugins>
80+
<!-- Shade Janino to avoid classpath conflicts -->
81+
<plugin>
82+
<groupId>org.apache.maven.plugins</groupId>
83+
<artifactId>maven-shade-plugin</artifactId>
84+
<executions>
85+
<execution>
86+
<id>shade-janino</id>
87+
<phase>package</phase>
88+
<goals>
89+
<goal>shade</goal>
90+
</goals>
91+
<configuration>
92+
<artifactSet>
93+
<includes>
94+
<include>org.codehaus.janino:janino</include>
95+
<include>org.codehaus.janino:commons-compiler</include>
96+
</includes>
97+
</artifactSet>
98+
<relocations>
99+
<relocation>
100+
<pattern>org.codehaus.janino</pattern>
101+
<shadedPattern>org.apache.fluss.shaded.org.codehaus.janino</shadedPattern>
102+
</relocation>
103+
<relocation>
104+
<pattern>org.codehaus.commons</pattern>
105+
<shadedPattern>org.apache.fluss.shaded.org.codehaus.commons</shadedPattern>
106+
</relocation>
107+
</relocations>
108+
<filters>
109+
<filter>
110+
<artifact>*:*</artifact>
111+
<excludes>
112+
<exclude>META-INF/*.SF</exclude>
113+
<exclude>META-INF/*.DSA</exclude>
114+
<exclude>META-INF/*.RSA</exclude>
115+
</excludes>
116+
</filter>
117+
</filters>
118+
</configuration>
119+
</execution>
120+
</executions>
121+
</plugin>
122+
</plugins>
123+
</build>
124+
</project>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.fluss.codegen;
20+
21+
import org.apache.fluss.annotation.Internal;
22+
import org.apache.fluss.exception.FlussRuntimeException;
23+
24+
/**
25+
* Exception for all errors occurring during code generation.
26+
*
27+
* <p>This exception is thrown when:
28+
*
29+
* <ul>
30+
* <li>Generated code fails to compile (syntax errors, missing imports, etc.)
31+
* <li>Unsupported data types are encountered during code generation
32+
* <li>Generated class fails to instantiate
33+
* </ul>
34+
*/
35+
@Internal
36+
public class CodeGenException extends FlussRuntimeException {
37+
38+
private static final long serialVersionUID = 1L;
39+
40+
public CodeGenException(String message) {
41+
super(message);
42+
}
43+
44+
public CodeGenException(String message, Throwable cause) {
45+
super(message, cause);
46+
}
47+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.fluss.codegen;
20+
21+
import org.apache.fluss.utils.InstantiationUtils;
22+
23+
import java.io.Serializable;
24+
import java.util.ArrayList;
25+
import java.util.LinkedHashSet;
26+
import java.util.List;
27+
import java.util.concurrent.atomic.AtomicLong;
28+
29+
/**
30+
* The context for code generator, maintaining various reusable statements that could be inserted
31+
* into different code sections in the final generated class.
32+
*/
33+
public class CodeGeneratorContext {
34+
35+
private static final AtomicLong NAME_COUNTER = new AtomicLong(0);
36+
37+
/** Holding a list of objects that could be passed into generated class. */
38+
private final List<Object> references = new ArrayList<>();
39+
40+
/** Set of member statements that will be added only once. */
41+
private final LinkedHashSet<String> reusableMemberStatements = new LinkedHashSet<>();
42+
43+
/** Set of constructor statements that will be added only once. */
44+
private final LinkedHashSet<String> reusableInitStatements = new LinkedHashSet<>();
45+
46+
public CodeGeneratorContext() {}
47+
48+
/**
49+
* Adds a reusable member field statement to the member area.
50+
*
51+
* @param memberStatement the member field declare statement
52+
*/
53+
public void addReusableMember(String memberStatement) {
54+
reusableMemberStatements.add(memberStatement);
55+
}
56+
57+
/**
58+
* Adds a reusable Object to the member area of the generated class. The object must be
59+
* Serializable.
60+
*
61+
* @param obj the object to be added to the generated class (must be Serializable)
62+
* @param fieldNamePrefix prefix field name of the generated member field term
63+
* @param fieldTypeTerm field type class name
64+
* @return the generated unique field term
65+
*/
66+
public <T extends Serializable> String addReusableObject(
67+
T obj, String fieldNamePrefix, String fieldTypeTerm) {
68+
String fieldTerm = newName(fieldNamePrefix);
69+
addReusableObjectInternal(obj, fieldTerm, fieldTypeTerm);
70+
return fieldTerm;
71+
}
72+
73+
private <T extends Serializable> void addReusableObjectInternal(
74+
T obj, String fieldTerm, String fieldTypeTerm) {
75+
int idx = references.size();
76+
// make a deep copy of the object
77+
try {
78+
Object objCopy = InstantiationUtils.clone(obj);
79+
references.add(objCopy);
80+
} catch (Exception e) {
81+
throw new CodeGenException("Failed to clone object: " + obj, e);
82+
}
83+
84+
reusableMemberStatements.add("private transient " + fieldTypeTerm + " " + fieldTerm + ";");
85+
reusableInitStatements.add(
86+
fieldTerm + " = ((" + fieldTypeTerm + ") references[" + idx + "]);");
87+
}
88+
89+
/** Adds a reusable init statement which will be placed in constructor. */
90+
public void addReusableInitStatement(String statement) {
91+
reusableInitStatements.add(statement);
92+
}
93+
94+
/**
95+
* @return code block of statements that need to be placed in the member area of the class
96+
*/
97+
public String reuseMemberCode() {
98+
return String.join("\n", reusableMemberStatements);
99+
}
100+
101+
/**
102+
* @return code block of statements that need to be placed in the constructor
103+
*/
104+
public String reuseInitCode() {
105+
return String.join("\n", reusableInitStatements);
106+
}
107+
108+
/**
109+
* @return the list of reference objects
110+
*/
111+
public Object[] getReferences() {
112+
return references.toArray();
113+
}
114+
115+
/**
116+
* Generates a new unique name with the given prefix.
117+
*
118+
* @param name the name prefix
119+
* @return a unique name
120+
*/
121+
public static String newName(String name) {
122+
return name + "$" + NAME_COUNTER.getAndIncrement();
123+
}
124+
}

0 commit comments

Comments
 (0)