JavaAstCodec.java
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.pattern.generator;
import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.ClassOrInterfaceModifier;
import org.apache.doris.nereids.pattern.generator.javaast.ClassOrInterfaceType;
import org.apache.doris.nereids.pattern.generator.javaast.EnumConstant;
import org.apache.doris.nereids.pattern.generator.javaast.EnumDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.FieldDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.IdentifyTypeArgumentsPair;
import org.apache.doris.nereids.pattern.generator.javaast.ImportDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.InterfaceDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.MethodDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.QualifiedName;
import org.apache.doris.nereids.pattern.generator.javaast.TypeArgument;
import org.apache.doris.nereids.pattern.generator.javaast.TypeArguments;
import org.apache.doris.nereids.pattern.generator.javaast.TypeBound;
import org.apache.doris.nereids.pattern.generator.javaast.TypeDeclaration;
import org.apache.doris.nereids.pattern.generator.javaast.TypeParameter;
import org.apache.doris.nereids.pattern.generator.javaast.TypeParameters;
import org.apache.doris.nereids.pattern.generator.javaast.TypeType;
import org.apache.doris.nereids.pattern.generator.javaast.TypeTypeOrVoid;
import org.apache.doris.nereids.pattern.generator.javaast.VariableDeclarator;
import org.apache.doris.nereids.pattern.generator.javaast.VariableDeclaratorId;
import org.apache.doris.nereids.pattern.generator.javaast.VariableDeclarators;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Reads and writes the java ast nodes of the pattern generator.
*
* <p>The ast is cached between builds (see {@link PatternGeneratorCache}), which needs a stable,
* dependency free and versioned encoding. The jdk serialization cannot be used here because the
* ast nodes hold {@link Optional} fields.
*
* <p>Adding a field to an ast node requires a new node tag or a format version bump in
* {@link PatternGeneratorCache}, otherwise an old cache would be read back incorrectly.
*/
public class JavaAstCodec {
private static final int NODE_CLASS = 1;
private static final int NODE_INTERFACE = 2;
private static final int NODE_ENUM = 3;
private static final int NODE_CLASS_OR_INTERFACE_TYPE = 4;
private static final int NODE_IDENTIFY_TYPE_ARGUMENTS_PAIR = 5;
private static final int NODE_TYPE_ARGUMENTS = 6;
private static final int NODE_TYPE_ARGUMENT = 7;
private static final int NODE_TYPE_PARAMETERS = 8;
private static final int NODE_TYPE_PARAMETER = 9;
private static final int NODE_TYPE_BOUND = 10;
private static final int NODE_TYPE_TYPE = 11;
private static final int NODE_TYPE_TYPE_OR_VOID = 12;
private static final int NODE_QUALIFIED_NAME = 13;
private static final int NODE_IMPORT = 14;
private static final int NODE_MODIFIER = 15;
private static final int NODE_ENUM_CONSTANT = 16;
private static final int NODE_FIELD = 17;
private static final int NODE_VARIABLE_DECLARATORS = 18;
private static final int NODE_VARIABLE_DECLARATOR = 19;
private static final int NODE_VARIABLE_DECLARATOR_ID = 20;
private static final int NODE_METHOD = 21;
/** read a list of type declarations. */
public static List<TypeDeclaration> readDeclarations(DataInputStream in) throws IOException {
int size = in.readInt();
List<TypeDeclaration> declarations = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
declarations.add(readTypeDeclaration(in));
}
return declarations;
}
/** write a list of type declarations. */
public static void writeDeclarations(DataOutputStream out, List<TypeDeclaration> declarations)
throws IOException {
out.writeInt(declarations.size());
for (TypeDeclaration declaration : declarations) {
writeTypeDeclaration(out, declaration);
}
}
/** read a single type declaration. */
public static TypeDeclaration readTypeDeclaration(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
Optional<QualifiedName> packageName = readOptional(in, JavaAstCodec::readQualifiedName);
List<ImportDeclaration> imports = readList(in, JavaAstCodec::readImport);
ClassOrInterfaceModifier modifiers = readModifier(in);
String name = in.readUTF();
List<TypeDeclaration> children = readList(in, JavaAstCodec::readTypeDeclaration);
switch (tag) {
case NODE_CLASS:
return new ClassDeclaration(packageName.orElse(null), imports, modifiers, name,
readOptional(in, JavaAstCodec::readTypeParameters).orElse(null),
readOptional(in, JavaAstCodec::readTypeType).orElse(null),
readList(in, JavaAstCodec::readTypeType),
readList(in, JavaAstCodec::readFieldDeclaration),
readList(in, JavaAstCodec::readMethodDeclaration),
children);
case NODE_INTERFACE:
return new InterfaceDeclaration(packageName.orElse(null), imports, modifiers, name,
readOptional(in, JavaAstCodec::readTypeParameters).orElse(null),
readList(in, JavaAstCodec::readTypeType),
children);
case NODE_ENUM:
return new EnumDeclaration(packageName.orElse(null), imports, modifiers, name,
readList(in, JavaAstCodec::readTypeType),
readList(in, JavaAstCodec::readEnumConstant),
children);
default:
throw new IOException("Unknown type declaration tag " + tag);
}
}
/** write a single type declaration. */
public static void writeTypeDeclaration(DataOutputStream out, TypeDeclaration declaration) throws IOException {
if (declaration instanceof ClassDeclaration) {
out.writeByte(NODE_CLASS);
} else if (declaration instanceof InterfaceDeclaration) {
out.writeByte(NODE_INTERFACE);
} else if (declaration instanceof EnumDeclaration) {
out.writeByte(NODE_ENUM);
} else {
throw new IOException("Unsupported type declaration " + declaration.getClass().getName());
}
writeOptional(out, declaration.packageName, JavaAstCodec::writeQualifiedName);
writeList(out, declaration.imports, JavaAstCodec::writeImport);
writeModifier(out, declaration.modifiers);
out.writeUTF(declaration.name);
writeList(out, declaration.children, JavaAstCodec::writeTypeDeclaration);
if (declaration instanceof ClassDeclaration) {
ClassDeclaration classDeclaration = (ClassDeclaration) declaration;
writeOptional(out, classDeclaration.typeParameters, JavaAstCodec::writeTypeParameters);
writeOptional(out, classDeclaration.extendsType, JavaAstCodec::writeTypeType);
writeList(out, classDeclaration.implementTypes, JavaAstCodec::writeTypeType);
writeList(out, classDeclaration.fieldDeclarations, JavaAstCodec::writeFieldDeclaration);
writeList(out, classDeclaration.methodDeclarations, JavaAstCodec::writeMethodDeclaration);
} else if (declaration instanceof InterfaceDeclaration) {
InterfaceDeclaration interfaceDeclaration = (InterfaceDeclaration) declaration;
writeOptional(out, interfaceDeclaration.typeParameters, JavaAstCodec::writeTypeParameters);
writeList(out, interfaceDeclaration.extendsTypes, JavaAstCodec::writeTypeType);
} else {
EnumDeclaration enumDeclaration = (EnumDeclaration) declaration;
writeList(out, enumDeclaration.implementTypes, JavaAstCodec::writeTypeType);
writeList(out, enumDeclaration.constants, JavaAstCodec::writeEnumConstant);
}
}
private static TypeType readTypeType(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_TYPE);
return new TypeType(readOptional(in, JavaAstCodec::readClassOrInterfaceType).orElse(null),
in.readBoolean() ? in.readUTF() : null);
}
private static void writeTypeType(DataOutputStream out, TypeType type) throws IOException {
out.writeByte(NODE_TYPE_TYPE);
writeOptional(out, type.classOrInterfaceType, JavaAstCodec::writeClassOrInterfaceType);
writeOptionalString(out, type.primitiveType);
}
private static TypeTypeOrVoid readTypeTypeOrVoid(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_TYPE_OR_VOID);
TypeType typeType = readOptional(in, JavaAstCodec::readTypeType).orElse(null);
return new TypeTypeOrVoid(typeType, in.readBoolean());
}
private static void writeTypeTypeOrVoid(DataOutputStream out, TypeTypeOrVoid typeTypeOrVoid) throws IOException {
out.writeByte(NODE_TYPE_TYPE_OR_VOID);
writeOptional(out, typeTypeOrVoid.typeType, JavaAstCodec::writeTypeType);
out.writeBoolean(typeTypeOrVoid.isVoid);
}
private static ClassOrInterfaceType readClassOrInterfaceType(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_CLASS_OR_INTERFACE_TYPE);
return new ClassOrInterfaceType(readList(in, JavaAstCodec::readIdentifyTypeArgumentsPair));
}
private static void writeClassOrInterfaceType(DataOutputStream out, ClassOrInterfaceType type) throws IOException {
out.writeByte(NODE_CLASS_OR_INTERFACE_TYPE);
writeList(out, type.identifyTypeArguments, JavaAstCodec::writeIdentifyTypeArgumentsPair);
}
private static IdentifyTypeArgumentsPair readIdentifyTypeArgumentsPair(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_IDENTIFY_TYPE_ARGUMENTS_PAIR);
String identifier = in.readUTF();
TypeArguments typeArguments = readOptional(in, JavaAstCodec::readTypeArguments).orElse(null);
return new IdentifyTypeArgumentsPair(identifier, typeArguments);
}
private static void writeIdentifyTypeArgumentsPair(DataOutputStream out, IdentifyTypeArgumentsPair pair)
throws IOException {
out.writeByte(NODE_IDENTIFY_TYPE_ARGUMENTS_PAIR);
out.writeUTF(pair.identifier);
writeOptional(out, pair.typeArguments, JavaAstCodec::writeTypeArguments);
}
private static TypeArguments readTypeArguments(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_ARGUMENTS);
return new TypeArguments(readList(in, JavaAstCodec::readTypeArgument));
}
private static void writeTypeArguments(DataOutputStream out, TypeArguments typeArguments) throws IOException {
out.writeByte(NODE_TYPE_ARGUMENTS);
writeList(out, typeArguments.typeArguments, JavaAstCodec::writeTypeArgument);
}
private static TypeArgument readTypeArgument(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_ARGUMENT);
TypeArgument.ArgType argType = TypeArgument.ArgType.values()[in.readUnsignedByte()];
TypeType typeType = readOptional(in, JavaAstCodec::readTypeType).orElse(null);
return new TypeArgument(argType, typeType);
}
private static void writeTypeArgument(DataOutputStream out, TypeArgument typeArgument) throws IOException {
out.writeByte(NODE_TYPE_ARGUMENT);
out.writeByte(typeArgument.getArgType().ordinal());
writeOptional(out, typeArgument.getTypeType(), JavaAstCodec::writeTypeType);
}
private static TypeParameters readTypeParameters(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_PARAMETERS);
return new TypeParameters(readList(in, JavaAstCodec::readTypeParameter));
}
private static void writeTypeParameters(DataOutputStream out, TypeParameters typeParameters) throws IOException {
out.writeByte(NODE_TYPE_PARAMETERS);
writeList(out, typeParameters.typeParameters, JavaAstCodec::writeTypeParameter);
}
private static TypeParameter readTypeParameter(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_PARAMETER);
String identifier = in.readUTF();
TypeBound typeBound = readOptional(in, JavaAstCodec::readTypeBound).orElse(null);
return new TypeParameter(identifier, typeBound);
}
private static void writeTypeParameter(DataOutputStream out, TypeParameter typeParameter) throws IOException {
out.writeByte(NODE_TYPE_PARAMETER);
out.writeUTF(typeParameter.identifier);
writeOptional(out, typeParameter.typeBound, JavaAstCodec::writeTypeBound);
}
private static TypeBound readTypeBound(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_TYPE_BOUND);
return new TypeBound(readList(in, JavaAstCodec::readTypeType));
}
private static void writeTypeBound(DataOutputStream out, TypeBound typeBound) throws IOException {
out.writeByte(NODE_TYPE_BOUND);
writeList(out, typeBound.types, JavaAstCodec::writeTypeType);
}
private static QualifiedName readQualifiedName(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_QUALIFIED_NAME);
return new QualifiedName(readStringList(in));
}
private static void writeQualifiedName(DataOutputStream out, QualifiedName qualifiedName) throws IOException {
out.writeByte(NODE_QUALIFIED_NAME);
writeStringList(out, qualifiedName.identifiers);
}
private static ImportDeclaration readImport(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_IMPORT);
boolean isStatic = in.readBoolean();
QualifiedName name = readQualifiedName(in);
return new ImportDeclaration(isStatic, name, in.readBoolean());
}
private static void writeImport(DataOutputStream out, ImportDeclaration importDeclaration) throws IOException {
out.writeByte(NODE_IMPORT);
out.writeBoolean(importDeclaration.isStatic);
writeQualifiedName(out, importDeclaration.name);
out.writeBoolean(importDeclaration.importAll);
}
private static ClassOrInterfaceModifier readModifier(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_MODIFIER);
return new ClassOrInterfaceModifier(in.readInt());
}
private static void writeModifier(DataOutputStream out, ClassOrInterfaceModifier modifier) throws IOException {
out.writeByte(NODE_MODIFIER);
out.writeInt(modifier.mod);
}
private static EnumConstant readEnumConstant(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_ENUM_CONSTANT);
return new EnumConstant(in.readUTF());
}
private static void writeEnumConstant(DataOutputStream out, EnumConstant enumConstant) throws IOException {
out.writeByte(NODE_ENUM_CONSTANT);
out.writeUTF(enumConstant.identifier);
}
private static FieldDeclaration readFieldDeclaration(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_FIELD);
TypeType type = readTypeType(in);
VariableDeclarators declarators = readVariableDeclarators(in);
return new FieldDeclaration(type, declarators);
}
private static void writeFieldDeclaration(DataOutputStream out, FieldDeclaration fieldDeclaration)
throws IOException {
out.writeByte(NODE_FIELD);
writeTypeType(out, fieldDeclaration.type);
writeVariableDeclarators(out, fieldDeclaration.variableDeclarators);
}
private static VariableDeclarators readVariableDeclarators(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_VARIABLE_DECLARATORS);
return new VariableDeclarators(readList(in, JavaAstCodec::readVariableDeclarator));
}
private static void writeVariableDeclarators(DataOutputStream out, VariableDeclarators declarators)
throws IOException {
out.writeByte(NODE_VARIABLE_DECLARATORS);
writeList(out, declarators.variableDeclarators, JavaAstCodec::writeVariableDeclarator);
}
private static VariableDeclarator readVariableDeclarator(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_VARIABLE_DECLARATOR);
return new VariableDeclarator(readVariableDeclaratorId(in));
}
private static void writeVariableDeclarator(DataOutputStream out, VariableDeclarator declarator)
throws IOException {
out.writeByte(NODE_VARIABLE_DECLARATOR);
writeVariableDeclaratorId(out, declarator.variableDeclaratorId);
}
private static VariableDeclaratorId readVariableDeclaratorId(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_VARIABLE_DECLARATOR_ID);
String identifier = in.readUTF();
return new VariableDeclaratorId(identifier, in.readInt());
}
private static void writeVariableDeclaratorId(DataOutputStream out, VariableDeclaratorId declaratorId)
throws IOException {
out.writeByte(NODE_VARIABLE_DECLARATOR_ID);
out.writeUTF(declaratorId.identifier);
out.writeInt(declaratorId.arrayDimension);
}
private static MethodDeclaration readMethodDeclaration(DataInputStream in) throws IOException {
int tag = in.readUnsignedByte();
checkTag(tag, NODE_METHOD);
TypeTypeOrVoid typeTypeOrVoid = readTypeTypeOrVoid(in);
String identifier = in.readUTF();
return new MethodDeclaration(typeTypeOrVoid, identifier, in.readInt());
}
private static void writeMethodDeclaration(DataOutputStream out, MethodDeclaration methodDeclaration)
throws IOException {
out.writeByte(NODE_METHOD);
writeTypeTypeOrVoid(out, methodDeclaration.typeTypeOrVoid);
out.writeUTF(methodDeclaration.identifier);
out.writeInt(methodDeclaration.paramNum);
}
private static <T> List<T> readList(DataInputStream in, ElementReader<T> reader) throws IOException {
int size = in.readInt();
List<T> elements = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
elements.add(reader.read(in));
}
return elements;
}
private static <T> void writeList(DataOutputStream out, List<T> elements, ElementWriter<T> writer)
throws IOException {
out.writeInt(elements.size());
for (T element : elements) {
writer.write(out, element);
}
}
private static <T> Optional<T> readOptional(DataInputStream in, ElementReader<T> reader) throws IOException {
return in.readBoolean() ? Optional.of(reader.read(in)) : Optional.empty();
}
private static <T> void writeOptional(DataOutputStream out, Optional<T> value, ElementWriter<T> writer)
throws IOException {
out.writeBoolean(value.isPresent());
if (value.isPresent()) {
writer.write(out, value.get());
}
}
private static void writeOptionalString(DataOutputStream out, Optional<String> value) throws IOException {
out.writeBoolean(value.isPresent());
if (value.isPresent()) {
out.writeUTF(value.get());
}
}
private static List<String> readStringList(DataInputStream in) throws IOException {
int size = in.readInt();
List<String> elements = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
elements.add(in.readUTF());
}
return elements;
}
private static void writeStringList(DataOutputStream out, List<String> elements) throws IOException {
out.writeInt(elements.size());
for (String element : elements) {
out.writeUTF(element);
}
}
private static void checkTag(int actual, int expected) throws IOException {
if (actual != expected) {
throw new IOException("Corrupted pattern generator cache, expected tag " + expected + " but got "
+ actual);
}
}
/** reads one element. */
private interface ElementReader<T> {
T read(DataInputStream in) throws IOException;
}
/** writes one element. */
private interface ElementWriter<T> {
void write(DataOutputStream out, T element) throws IOException;
}
}