You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2022/12/14 13:15:36 UTC
[ignite-3] branch main updated: IGNITE-17167 ConfigurationAsmGenerator decomposition (#1431)
This is an automated email from the ASF dual-hosted git repository.
ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 15aba58e97 IGNITE-17167 ConfigurationAsmGenerator decomposition (#1431)
15aba58e97 is described below
commit 15aba58e97fc9fba127c0a861a5b1050f215e13b
Author: Ivan Bessonov <be...@gmail.com>
AuthorDate: Wed Dec 14 16:15:30 2022 +0300
IGNITE-17167 ConfigurationAsmGenerator decomposition (#1431)
---
.../configuration/asm/AbstractAsmGenerator.java | 114 +
.../asm/ConfigurationAsmGenerator.java | 3074 +-------------------
.../asm/ConfigurationImplAsmGenerator.java | 932 ++++++
.../configuration/asm/DirectProxyAsmGenerator.java | 69 +-
.../configuration/asm/InnerNodeAsmGenerator.java | 1890 ++++++++++++
5 files changed, 3069 insertions(+), 3010 deletions(-)
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/AbstractAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/AbstractAsmGenerator.java
new file mode 100644
index 0000000000..d6a813b5cd
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/AbstractAsmGenerator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ignite.internal.configuration.asm;
+
+import com.facebook.presto.bytecode.ClassDefinition;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.apache.ignite.internal.configuration.tree.ConstructableTreeNode;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Class that holds constants and commonly used fields for generators.
+ */
+abstract class AbstractAsmGenerator {
+ /** {@link LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}. */
+ static final Method LAMBDA_METAFACTORY;
+
+ /** {@link ConstructableTreeNode#copy()}. */
+ static final Method COPY;
+
+ static {
+ try {
+ LAMBDA_METAFACTORY = LambdaMetafactory.class.getDeclaredMethod(
+ "metafactory",
+ Lookup.class,
+ String.class,
+ MethodType.class,
+ MethodType.class,
+ MethodHandle.class,
+ MethodType.class
+ );
+
+ COPY = ConstructableTreeNode.class.getDeclaredMethod("copy");
+ } catch (NoSuchMethodException nsme) {
+ throw new ExceptionInInitializerError(nsme);
+ }
+ }
+
+ /** This generator instance. */
+ final ConfigurationAsmGenerator cgen;
+
+ /** Configuration schema class. */
+ final Class<?> schemaClass;
+
+ /** Internal extensions of the configuration schema. */
+ final Set<Class<?>> internalExtensions;
+
+ /** Polymorphic extensions of the configuration schema. */
+ final Set<Class<?>> polymorphicExtensions;
+
+ /** Fields of the schema class. */
+ final List<Field> schemaFields;
+
+ /** Fields of internal extensions of the configuration schema. */
+ final Collection<Field> internalFields;
+
+ /** Fields of polymorphic extensions of the configuration schema. */
+ final Collection<Field> polymorphicFields;
+
+ /** Internal id field or {@code null} if it's not present. */
+ final Field internalIdField;
+
+ /**
+ * Constructor.
+ * Please refer to individual fields for comments.
+ */
+ AbstractAsmGenerator(
+ ConfigurationAsmGenerator cgen,
+ Class<?> schemaClass,
+ Set<Class<?>> internalExtensions,
+ Set<Class<?>> polymorphicExtensions,
+ List<Field> schemaFields,
+ Collection<Field> internalFields,
+ Collection<Field> polymorphicFields,
+ @Nullable Field internalIdField
+ ) {
+ this.cgen = cgen;
+ this.schemaClass = schemaClass;
+ this.internalExtensions = internalExtensions;
+ this.polymorphicExtensions = polymorphicExtensions;
+ this.schemaFields = schemaFields;
+ this.internalFields = internalFields;
+ this.polymorphicFields = polymorphicFields;
+ this.internalIdField = internalIdField;
+ }
+
+ /**
+ * Generates class definitions. Expected to be called once at most. There can be more than one definition in case when there's a need
+ * to generate an inner class, for example.
+ */
+ abstract List<ClassDefinition> generate();
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
index cd0a8f12c2..4fadddfbd4 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
@@ -17,54 +17,29 @@
package org.apache.ignite.internal.configuration.asm;
-import static com.facebook.presto.bytecode.Access.BRIDGE;
-import static com.facebook.presto.bytecode.Access.FINAL;
-import static com.facebook.presto.bytecode.Access.PRIVATE;
-import static com.facebook.presto.bytecode.Access.PUBLIC;
-import static com.facebook.presto.bytecode.Access.STATIC;
-import static com.facebook.presto.bytecode.Access.SYNTHETIC;
-import static com.facebook.presto.bytecode.Parameter.arg;
import static com.facebook.presto.bytecode.ParameterizedType.type;
import static com.facebook.presto.bytecode.ParameterizedType.typeFromJavaClassName;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantBoolean;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantClass;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantNull;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantString;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.inlineIf;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeDynamic;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeStatic;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.isNotNull;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.isNull;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newArray;
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newInstance;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.not;
-import static com.facebook.presto.bytecode.expression.BytecodeExpressions.set;
import static java.lang.invoke.MethodType.methodType;
import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singleton;
-import static java.util.EnumSet.of;
-import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
-import static org.apache.ignite.internal.configuration.asm.DirectProxyAsmGenerator.newDirectProxyLambda;
+import static org.apache.ignite.internal.configuration.asm.AbstractAsmGenerator.COPY;
+import static org.apache.ignite.internal.configuration.asm.AbstractAsmGenerator.LAMBDA_METAFACTORY;
import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.changeClassName;
import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.configurationClassName;
-import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.nodeClassName;
import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.viewClassName;
-import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.containsNameAnnotation;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.extensionsFields;
-import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.hasDefault;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isConfigValue;
-import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInjectedName;
-import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInternalId;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isNamedConfigValue;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfig;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfigInstance;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicId;
-import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isValue;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.polymorphicInstanceId;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.schemaFields;
import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
@@ -75,23 +50,14 @@ import static org.objectweb.asm.Type.getMethodType;
import static org.objectweb.asm.Type.getType;
import com.facebook.presto.bytecode.BytecodeBlock;
-import com.facebook.presto.bytecode.BytecodeNode;
import com.facebook.presto.bytecode.ClassDefinition;
import com.facebook.presto.bytecode.ClassGenerator;
import com.facebook.presto.bytecode.FieldDefinition;
import com.facebook.presto.bytecode.MethodDefinition;
import com.facebook.presto.bytecode.ParameterizedType;
-import com.facebook.presto.bytecode.Variable;
-import com.facebook.presto.bytecode.control.IfStatement;
import com.facebook.presto.bytecode.expression.BytecodeExpression;
-import java.io.Serializable;
-import java.lang.invoke.LambdaMetafactory;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -99,233 +65,37 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Queue;
import java.util.Set;
-import java.util.UUID;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
-import org.apache.ignite.configuration.ConfigurationProperty;
-import org.apache.ignite.configuration.ConfigurationTree;
-import org.apache.ignite.configuration.ConfigurationValue;
import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdException;
-import org.apache.ignite.configuration.NamedConfigurationTree;
-import org.apache.ignite.configuration.NamedListView;
import org.apache.ignite.configuration.RootKey;
import org.apache.ignite.configuration.annotation.AbstractConfiguration;
import org.apache.ignite.configuration.annotation.Config;
import org.apache.ignite.configuration.annotation.ConfigurationRoot;
-import org.apache.ignite.configuration.annotation.InjectedName;
import org.apache.ignite.configuration.annotation.InternalConfiguration;
-import org.apache.ignite.configuration.annotation.Name;
import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.PolymorphicConfig;
import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
import org.apache.ignite.configuration.annotation.PolymorphicId;
-import org.apache.ignite.internal.configuration.ConfigurationNode;
-import org.apache.ignite.internal.configuration.ConfigurationTreeWrapper;
import org.apache.ignite.internal.configuration.DynamicConfiguration;
import org.apache.ignite.internal.configuration.DynamicConfigurationChanger;
-import org.apache.ignite.internal.configuration.DynamicProperty;
-import org.apache.ignite.internal.configuration.NamedListConfiguration;
import org.apache.ignite.internal.configuration.TypeUtils;
-import org.apache.ignite.internal.configuration.direct.DirectPropertyProxy;
-import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
-import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
-import org.apache.ignite.internal.configuration.tree.ConstructableTreeNode;
import org.apache.ignite.internal.configuration.tree.InnerNode;
import org.apache.ignite.internal.configuration.tree.NamedListNode;
import org.apache.ignite.internal.configuration.util.ConfigurationUtil;
-import org.apache.ignite.internal.util.ArrayUtils;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Handle;
-import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* This class is responsible for generating internal implementation classes for configuration schemas. It uses classes from {@code bytecode}
- * module to achieve this goal, like {@link ClassGenerator}, for examples.
+ * module to achieve this goal, like {@link ClassGenerator}, for example.
*/
-// TODO: IGNITE-17167 Split into classes/methods for regular/internal/polymorphic/abstract configuration
public class ConfigurationAsmGenerator {
- /** {@link DynamicConfiguration#DynamicConfiguration} constructor. */
- private static final Constructor<?> DYNAMIC_CONFIGURATION_CTOR;
-
- /** {@link LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}. */
- public static final Method LAMBDA_METAFACTORY;
-
- /** {@link Consumer#accept(Object)}. */
- private static final Method ACCEPT;
-
- /** {@link ConfigurationVisitor#visitLeafNode(String, Serializable)}. */
- private static final Method VISIT_LEAF;
-
- /** {@link ConfigurationVisitor#visitInnerNode(String, InnerNode)}. */
- private static final Method VISIT_INNER;
-
- /** {@link ConfigurationVisitor#visitNamedListNode(String, NamedListNode)}. */
- private static final Method VISIT_NAMED;
-
- /** {@link ConfigurationSource#unwrap(Class)}. */
- private static final Method UNWRAP;
-
- /** {@link ConfigurationSource#descend(ConstructableTreeNode)}. */
- private static final Method DESCEND;
-
- /** {@link ConstructableTreeNode#copy()}. */
- private static final Method COPY;
-
- /** {@link InnerNode#internalId()}. */
- private static final Method INTERNAL_ID;
-
- /** {@code DynamicConfiguration#add} method. */
- private static final Method DYNAMIC_CONFIGURATION_ADD_MTD;
-
- /** {@link Objects#requireNonNull(Object, String)}. */
- private static final Method REQUIRE_NON_NULL;
-
- /** {@link Class#getName} method. */
- private static final Method CLASS_GET_NAME_MTD;
-
- /** {@link String#equals} method. */
- private static final Method STRING_EQUALS_MTD;
-
- /** {@link ConfigurationSource#polymorphicTypeId} method. */
- private static final Method POLYMORPHIC_TYPE_ID_MTD;
-
- /** {@link InnerNode#constructDefault} method. */
- private static final Method CONSTRUCT_DEFAULT_MTD;
-
- /** {@code ConfigurationNode#refreshValue} method. */
- private static final Method REFRESH_VALUE_MTD;
-
- /** {@code DynamicConfiguration#addMember} method. */
- private static final Method ADD_MEMBER_MTD;
-
- /** {@code DynamicConfiguration#removeMember} method. */
- private static final Method REMOVE_MEMBER_MTD;
-
- /** {@link InnerNode#specificNode} method. */
- private static final Method SPECIFIC_NODE_MTD;
-
- /** {@link DynamicConfiguration#specificConfigTree} method. */
- private static final Method SPECIFIC_CONFIG_TREE_MTD;
-
- /** {@link ConfigurationUtil#addDefaults}. */
- private static final Method ADD_DEFAULTS_MTD;
-
- /** {@link InnerNode#setInjectedNameFieldValue}. */
- private static final Method SET_INJECTED_NAME_FIELD_VALUE_MTD;
-
- /** {@code ConfigurationNode#currentValue}. */
- private static final Method CURRENT_VALUE_MTD;
-
- /** {@link DynamicConfiguration#isRemovedFromNamedList}. */
- private static final Method IS_REMOVED_FROM_NAMED_LIST_MTD;
-
- /** {@link InnerNode#isPolymorphic}. */
- private static final Method IS_POLYMORPHIC_MTD;
-
- /** {@link InnerNode#internalSchemaTypes}. */
- private static final Method INTERNAL_SCHEMA_TYPES_MTD;
-
- /** {@code Node#convert} method name. */
- private static final String CONVERT_MTD_NAME = "convert";
-
- /** {@link ConstructableTreeNode#construct(String, ConfigurationSource, boolean)} method name. */
- private static final String CONSTRUCT_MTD_NAME = "construct";
-
- /** Field name for method {@link DynamicConfiguration#internalConfigTypes}. */
- private static final String INTERNAL_CONFIG_TYPES_FIELD_NAME = "_internalConfigTypes";
-
- static {
- try {
- LAMBDA_METAFACTORY = LambdaMetafactory.class.getDeclaredMethod(
- "metafactory",
- Lookup.class,
- String.class,
- MethodType.class,
- MethodType.class,
- MethodHandle.class,
- MethodType.class
- );
-
- ACCEPT = Consumer.class.getDeclaredMethod("accept", Object.class);
-
- VISIT_LEAF = ConfigurationVisitor.class
- .getDeclaredMethod("visitLeafNode", String.class, Serializable.class);
-
- VISIT_INNER = ConfigurationVisitor.class
- .getDeclaredMethod("visitInnerNode", String.class, InnerNode.class);
-
- VISIT_NAMED = ConfigurationVisitor.class
- .getDeclaredMethod("visitNamedListNode", String.class, NamedListNode.class);
-
- UNWRAP = ConfigurationSource.class.getDeclaredMethod("unwrap", Class.class);
-
- DESCEND = ConfigurationSource.class.getDeclaredMethod("descend", ConstructableTreeNode.class);
-
- COPY = ConstructableTreeNode.class.getDeclaredMethod("copy");
-
- INTERNAL_ID = InnerNode.class.getDeclaredMethod("internalId");
-
- DYNAMIC_CONFIGURATION_CTOR = DynamicConfiguration.class.getDeclaredConstructor(
- List.class,
- String.class,
- RootKey.class,
- DynamicConfigurationChanger.class,
- boolean.class
- );
-
- DYNAMIC_CONFIGURATION_ADD_MTD = DynamicConfiguration.class.getDeclaredMethod(
- "add",
- ConfigurationProperty.class
- );
-
- REQUIRE_NON_NULL = Objects.class.getDeclaredMethod("requireNonNull", Object.class, String.class);
-
- CLASS_GET_NAME_MTD = Class.class.getDeclaredMethod("getName");
-
- STRING_EQUALS_MTD = String.class.getDeclaredMethod("equals", Object.class);
-
- POLYMORPHIC_TYPE_ID_MTD = ConfigurationSource.class.getDeclaredMethod("polymorphicTypeId", String.class);
-
- CONSTRUCT_DEFAULT_MTD = InnerNode.class.getDeclaredMethod("constructDefault", String.class);
-
- REFRESH_VALUE_MTD = ConfigurationNode.class.getDeclaredMethod("refreshValue");
-
- ADD_MEMBER_MTD = DynamicConfiguration.class.getDeclaredMethod("addMember", Map.class, ConfigurationProperty.class);
-
- REMOVE_MEMBER_MTD = DynamicConfiguration.class.getDeclaredMethod("removeMember", Map.class, ConfigurationProperty.class);
-
- SPECIFIC_NODE_MTD = InnerNode.class.getDeclaredMethod("specificNode");
-
- SPECIFIC_CONFIG_TREE_MTD = DynamicConfiguration.class.getDeclaredMethod("specificConfigTree");
-
- ADD_DEFAULTS_MTD = ConfigurationUtil.class.getDeclaredMethod("addDefaults", InnerNode.class);
-
- SET_INJECTED_NAME_FIELD_VALUE_MTD = InnerNode.class.getDeclaredMethod("setInjectedNameFieldValue", String.class);
-
- CURRENT_VALUE_MTD = ConfigurationNode.class.getDeclaredMethod("currentValue");
-
- IS_REMOVED_FROM_NAMED_LIST_MTD = DynamicConfiguration.class.getDeclaredMethod("isRemovedFromNamedList");
-
- IS_POLYMORPHIC_MTD = InnerNode.class.getDeclaredMethod("isPolymorphic");
-
- INTERNAL_SCHEMA_TYPES_MTD = InnerNode.class.getDeclaredMethod("internalSchemaTypes");
- } catch (NoSuchMethodException nsme) {
- throw new ExceptionInInitializerError(nsme);
- }
- }
-
/** Information about schema classes - bunch of names and dynamically compiled internal classes. */
private final Map<Class<?>, SchemaClassesInfo> schemasInfo = new HashMap<>();
@@ -462,7 +232,8 @@ public class ConfigurationAsmGenerator {
schemas.add(schemaClass);
- ClassDefinition innerNodeClassDef = createNodeClass(
+ classDefs.addAll(new InnerNodeAsmGenerator(
+ this,
schemaClass,
internalExtensions,
polymorphicExtensions,
@@ -470,11 +241,10 @@ public class ConfigurationAsmGenerator {
internalExtensionsFields,
polymorphicExtensionsFields,
internalIdField
- );
-
- classDefs.add(innerNodeClassDef);
+ ).generate());
- ClassDefinition cfgImplClassDef = createCfgImplClass(
+ classDefs.addAll(new ConfigurationImplAsmGenerator(
+ this,
schemaClass,
internalExtensions,
polymorphicExtensions,
@@ -482,45 +252,16 @@ public class ConfigurationAsmGenerator {
internalExtensionsFields,
polymorphicExtensionsFields,
internalIdField
- );
-
- classDefs.add(cfgImplClassDef);
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- // Only the fields of a specific instance of a polymorphic configuration.
- Collection<Field> polymorphicFields = polymorphicExtensionsFields.stream()
- .filter(f -> f.getDeclaringClass() == polymorphicExtension)
- .collect(toList());
-
- classDefs.add(createPolymorphicExtensionNodeClass(
- schemaClass,
- polymorphicExtension,
- innerNodeClassDef,
- schemaFields,
- polymorphicFields,
- internalIdField
- ));
-
- classDefs.add(createPolymorphicExtensionCfgImplClass(
- schemaClass,
- polymorphicExtension,
- cfgImplClassDef,
- schemaFields,
- polymorphicFields,
- internalIdField
- ));
- }
+ ).generate());
- ClassDefinition directProxyClassDef = new DirectProxyAsmGenerator(
+ classDefs.addAll(new DirectProxyAsmGenerator(
this,
schemaClass,
internalExtensions,
schemaFields,
internalExtensionsFields,
internalIdField
- ).generate();
-
- classDefs.add(directProxyClassDef);
+ ).generate());
}
Map<String, Class<?>> definedClasses = generator.defineClasses(classDefs);
@@ -570,2647 +311,138 @@ public class ConfigurationAsmGenerator {
}
/**
- * Construct a {@link InnerNode} definition for a configuration schema.
+ * Copies field into itself or instantiates it if the field is null. Code like: {@code this.field == null ? new ValueNode() :
+ * (ValueNode)this.field.copy();}.
*
- * @param schemaClass Configuration schema class.
- * @param internalExtensions Internal extensions of the configuration schema.
- * @param polymorphicExtensions Polymorphic extensions of the configuration schema.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFields Fields of polymorphic extensions of the configuration schema.
- * @param internalIdField Internal id field or {@code null} if it's not present.
- * @return Constructed {@link InnerNode} definition for the configuration schema.
+ * @param schemaField Configuration schema class field.
+ * @param getFieldCode Bytecode of getting the field, for example: {@code this.field} or {@code this.field.field};
+ * @return Bytecode expression.
*/
- private ClassDefinition createNodeClass(
- Class<?> schemaClass,
- Set<Class<?>> internalExtensions,
- Set<Class<?>> polymorphicExtensions,
- List<Field> schemaFields,
- Collection<Field> internalFields,
- Collection<Field> polymorphicFields,
- @Nullable Field internalIdField
- ) {
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
-
- // Node class definition.
- ClassDefinition classDef = new ClassDefinition(
- of(PUBLIC, FINAL),
- internalName(schemaClassInfo.nodeClassName),
- type(InnerNode.class),
- nodeClassInterfaces(schemaClass, internalExtensions)
- );
-
- // Spec fields.
- Map<Class<?>, FieldDefinition> specFields = new HashMap<>();
-
- int i = 0;
-
- for (Class<?> clazz : concat(List.of(schemaClass), internalExtensions, polymorphicExtensions)) {
- specFields.put(clazz, classDef.declareField(of(PRIVATE, FINAL), "_spec" + i++, clazz));
- }
-
- // Define the rest of the fields.
- Map<String, FieldDefinition> fieldDefs = new HashMap<>();
-
- // To store the id of the polymorphic configuration instance.
- FieldDefinition polymorphicTypeIdFieldDef = null;
-
- // Field with @InjectedName.
- FieldDefinition injectedNameFieldDef = null;
-
- for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
- String fieldName = fieldName(schemaField);
-
- FieldDefinition fieldDef = addNodeField(classDef, schemaField, fieldName);
-
- fieldDefs.put(fieldName, fieldDef);
-
- if (isPolymorphicId(schemaField)) {
- polymorphicTypeIdFieldDef = fieldDef;
- } else if (isInjectedName(schemaField)) {
- injectedNameFieldDef = fieldDef;
- }
- }
-
- // org.apache.ignite.internal.configuration.tree.InnerNode#schemaType
- addNodeSchemaTypeMethod(classDef, schemaClass, polymorphicExtensions, polymorphicTypeIdFieldDef);
-
- FieldDefinition internalSchemaTypesFieldDef = null;
-
- if (!internalExtensions.isEmpty()) {
- internalSchemaTypesFieldDef = classDef.declareField(
- of(PRIVATE, FINAL),
- "_" + INTERNAL_SCHEMA_TYPES_MTD.getName(),
- Class[].class
- );
- }
-
- // Constructor.
- addNodeConstructor(
- classDef,
- specFields,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFields,
- internalExtensions,
- internalSchemaTypesFieldDef
- );
-
- // Add view method for internal id.
- if (internalIdField != null) {
- addNodeInternalIdMethod(classDef, internalIdField);
- }
-
- // VIEW and CHANGE methods.
- for (Field schemaField : concat(schemaFields, internalFields)) {
- String fieldName = schemaField.getName();
-
- FieldDefinition fieldDef = fieldDefs.get(fieldName);
-
- addNodeViewMethod(
- classDef,
- schemaField,
- viewMtd -> getThisFieldCode(viewMtd, fieldDef),
- null
- );
-
- // Read only.
- if (isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
- continue;
- }
-
- // Add change methods.
- MethodDefinition changeMtd0 = addNodeChangeMethod(
- classDef,
- schemaField,
- changeMtd -> getThisFieldCode(changeMtd, fieldDef),
- (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, fieldDef),
- null
- );
-
- addNodeChangeBridgeMethod(classDef, changeClassName(schemaField.getDeclaringClass()), changeMtd0);
- }
-
- Map<Class<?>, List<Field>> polymorphicFieldsByExtension = Map.of();
-
- MethodDefinition changePolymorphicTypeIdMtd = null;
-
- if (!polymorphicExtensions.isEmpty()) {
- assert polymorphicTypeIdFieldDef != null : schemaClass.getName();
-
- addNodeSpecificNodeMethod(classDef, polymorphicExtensions, polymorphicTypeIdFieldDef);
-
- changePolymorphicTypeIdMtd = addNodeChangePolymorphicTypeIdMethod(
- classDef,
- fieldDefs,
- polymorphicExtensions,
- polymorphicFields,
- polymorphicTypeIdFieldDef
- );
-
- addNodeConvertMethods(classDef, schemaClass, polymorphicExtensions, changePolymorphicTypeIdMtd);
-
- polymorphicFieldsByExtension = new LinkedHashMap<>();
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- polymorphicFieldsByExtension.put(
- polymorphicExtension,
- polymorphicFields.stream()
- .filter(f -> polymorphicExtension.equals(f.getDeclaringClass()))
- .collect(toList())
- );
- }
- }
-
- // traverseChildren
- addNodeTraverseChildrenMethod(
- classDef,
- schemaClass,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFieldsByExtension,
- polymorphicTypeIdFieldDef
- );
-
- // traverseChild
- addNodeTraverseChildMethod(
- classDef,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFieldsByExtension,
- polymorphicTypeIdFieldDef
- );
-
- // construct
- addNodeConstructMethod(
- classDef,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFieldsByExtension,
- polymorphicTypeIdFieldDef,
- changePolymorphicTypeIdMtd
- );
+ BytecodeExpression newOrCopyNodeField(Field schemaField, BytecodeExpression getFieldCode) {
+ ParameterizedType nodeType = typeFromJavaClassName(schemasInfo.get(schemaField.getType()).nodeClassName);
- // constructDefault
- addNodeConstructDefaultMethod(
- schemaClass,
- classDef,
- specFields,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFieldsByExtension,
- polymorphicTypeIdFieldDef
+ // this.field == null ? new ValueNode() : (ValueNode)this.field.copy();
+ return inlineIf(
+ isNull(getFieldCode),
+ newInstance(nodeType),
+ copyNodeField(schemaField, getFieldCode)
);
+ }
- if (injectedNameFieldDef != null) {
- addInjectedNameFieldMethods(classDef, injectedNameFieldDef);
- }
-
- if (polymorphicTypeIdFieldDef != null) {
- addIsPolymorphicMethod(classDef);
- }
-
- if (internalSchemaTypesFieldDef != null) {
- addInternalSchemaTypesMethod(classDef, internalSchemaTypesFieldDef);
- }
-
- if (schemaClass.getSuperclass().isAnnotationPresent(AbstractConfiguration.class)) {
- addIsExtendAbstractConfigurationMethod(classDef);
- }
+ /**
+ * Copies field into itself. Code like: {@code (ValueNode)this.field.copy();}.
+ *
+ * @param schemaField Configuration schema class field.
+ * @param getFieldCode Bytecode of getting the field, for example: {@code this.field} or {@code this.field.field};
+ * @return Bytecode expression.
+ */
+ BytecodeExpression copyNodeField(Field schemaField, BytecodeExpression getFieldCode) {
+ ParameterizedType nodeType = isNamedConfigValue(schemaField)
+ ? type(NamedListNode.class) : typeFromJavaClassName(schemasInfo.get(schemaField.getType()).nodeClassName);
- return classDef;
+ // (ValueNode)this.field.copy();
+ return getFieldCode.invoke(COPY).cast(nodeType);
}
/**
- * Add {@link InnerNode#schemaType} method implementation to the class.
+ * Creates {@code *Node::new} lambda expression with {@link Supplier} type.
*
- * @param classDef Class definition.
- * @param schemaClass Configuration schema class.
- * @param polymorphicExtensions Polymorphic extensions of the configuration schema.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ * @param nodeClassName Name of the {@code *Node} class.
+ * @return InvokeDynamic bytecode expression.
*/
- private static void addNodeSchemaTypeMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- @Nullable FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition schemaTypeMtd = classDef.declareMethod(
- of(PUBLIC),
- "schemaType",
- type(Class.class)
+ private static BytecodeExpression newNamedListElementLambda(String nodeClassName) {
+ return invokeDynamic(
+ LAMBDA_METAFACTORY,
+ asList(
+ getMethodType(getType(Object.class)),
+ new Handle(
+ H_NEWINVOKESPECIAL,
+ internalName(nodeClassName),
+ "<init>",
+ getMethodDescriptor(Type.VOID_TYPE),
+ false
+ ),
+ getMethodType(typeFromJavaClassName(nodeClassName).getAsmType())
+ ),
+ "get",
+ methodType(Supplier.class)
);
-
- BytecodeBlock mtdBody = schemaTypeMtd.getBody();
-
- if (polymorphicExtensions.isEmpty()) {
- mtdBody.append(constantClass(schemaClass)).retObject();
- } else {
- assert polymorphicTypeIdFieldDef != null : classDef.getName();
-
- StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(schemaTypeMtd, polymorphicTypeIdFieldDef);
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- switchBuilderTypeId.addCase(
- polymorphicInstanceId(polymorphicExtension),
- constantClass(polymorphicExtension).ret()
- );
- }
-
- mtdBody.append(switchBuilderTypeId.build());
- }
}
/**
- * Declares field that corresponds to configuration value. Depending on the schema, 5 options possible:
- * <ul>
- * <li>
- * {@code @Value public type fieldName}<br/>becomes<br/>
- * {@code public BoxedType fieldName}
- * </li>
- * <li>
- * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
- * {@code public MyNode fieldName}
- * </li>
- * <li>
- * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
- * {@code public NamedListNode fieldName}
- * </li>
- * <li>
- * {@code @PolymorphicId public String fieldName}<br/>becomes<br/>
- * {@code public String fieldName}
- * </li>
- * <li>
- * {@code @InjectedName public String fieldName}<br/>becomes<br/>
- * {@code public String fieldName}
- * </li>
- * </ul>
+ * Replaces first letter in string with its upper-cased variant.
*
- * @param classDef Node class definition.
- * @param schemaField Configuration Schema class field.
- * @param fieldName Field name.
- * @return Declared field definition.
- * @throws IllegalArgumentException If an unsupported {@code schemaField} was passed.
+ * @param name Some string.
+ * @return Capitalized version of passed string.
*/
- private FieldDefinition addNodeField(ClassDefinition classDef, Field schemaField, String fieldName) {
- Class<?> schemaFieldClass = schemaField.getType();
-
- ParameterizedType nodeFieldType;
-
- if (isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
- nodeFieldType = type(box(schemaFieldClass));
- } else if (isConfigValue(schemaField)) {
- nodeFieldType = typeFromJavaClassName(schemasInfo.get(schemaFieldClass).nodeClassName);
- } else if (isNamedConfigValue(schemaField)) {
- nodeFieldType = type(NamedListNode.class);
- } else {
- throw new IllegalArgumentException("Unsupported field: " + schemaField);
- }
-
- return classDef.declareField(of(PUBLIC), fieldName, nodeFieldType);
+ private static String capitalize(String name) {
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
}
/**
- * Implements default constructor for the node class. It initializes {@code _spec} field and every other field that represents named
- * list configuration.
+ * Converts a public class name into an internal class name, replacing dots with slashes.
*
- * @param classDef Node class definition.
- * @param specFields Definition of fields for the {@code _spec#} fields of the node class. Mapping: configuration schema class -> {@code
- * _spec#} field.
- * @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFields Fields of polymorphic extensions of the configuration schema.
- * @param internalExtensions Internal extensions of the configuration schema.
- * @param internalSchemaTypesFieldDef Final field which stores {@code internalExtensions}.
+ * @param className Class name (with package).
+ * @return Internal class name.
+ * @see Type#getInternalName(Class)
*/
- private void addNodeConstructor(
- ClassDefinition classDef,
- Map<Class<?>, FieldDefinition> specFields,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Collection<Field> polymorphicFields,
- Set<Class<?>> internalExtensions,
- @Nullable FieldDefinition internalSchemaTypesFieldDef
- ) {
- MethodDefinition ctor = classDef.declareConstructor(of(PUBLIC));
-
- BytecodeBlock ctorBody = ctor.getBody();
-
- // super();
- ctorBody
- .append(ctor.getThis())
- .invokeConstructor(InnerNode.class);
-
- // this._spec# = new MyConfigurationSchema();
- for (Map.Entry<Class<?>, FieldDefinition> e : specFields.entrySet()) {
- ctorBody.append(ctor.getThis().setField(e.getValue(), newInstance(e.getKey())));
- }
-
- for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
- if (!isNamedConfigValue(schemaField)) {
- continue;
- }
-
- FieldDefinition fieldDef = fieldDefs.get(fieldName(schemaField));
-
- // this.values = new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName");
- ctorBody.append(setThisFieldCode(ctor, newNamedListNode(schemaField), fieldDef));
- }
-
- if (!internalExtensions.isEmpty()) {
- assert internalSchemaTypesFieldDef != null : classDef;
-
- // Class[] tmp;
- Variable tmpVar = ctor.getScope().createTempVariable(Class[].class);
-
- BytecodeBlock initInternalSchemaTypesField = new BytecodeBlock();
-
- // tmp = new Class[size];
- initInternalSchemaTypesField.append(tmpVar.set(newArray(type(Class[].class), internalExtensions.size())));
+ static String internalName(String className) {
+ return className.replace('.', '/');
+ }
- int i = 0;
+ /**
+ * Creates boxed version of the class.
+ *
+ * @param clazz Maybe primitive class.
+ * @return Not primitive class that represents parameter class.
+ */
+ public static Class<?> box(Class<?> clazz) {
+ Class<?> boxed = TypeUtils.boxed(clazz);
- for (Class<?> extension : internalExtensions) {
- // tmp[i] = InternalTableConfigurationSchema.class;
- initInternalSchemaTypesField.append(set(
- tmpVar,
- constantInt(i++),
- constantClass(extension)
- ));
- }
+ return boxed == null ? clazz : boxed;
+ }
- // this._internalConfigTypes = tmp;
- initInternalSchemaTypesField.append(setThisFieldCode(ctor, tmpVar, internalSchemaTypesFieldDef));
+ /**
+ * Get interfaces for {@link InnerNode} definition for a configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link InnerNode} definition for a configuration schema.
+ */
+ static ParameterizedType[] nodeClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
+ Collection<ParameterizedType> res = new ArrayList<>();
- ctorBody.append(initInternalSchemaTypesField);
+ for (Class<?> cls : concat(List.of(schemaClass), schemaExtensions)) {
+ res.add(typeFromJavaClassName(viewClassName(cls)));
+ res.add(typeFromJavaClassName(changeClassName(cls)));
}
- // return;
- ctorBody.ret();
+ return res.toArray(ParameterizedType[]::new);
}
/**
- * Generates method with the same name as the field, that calls {@link InnerNode#internalId()}.
+ * Get interfaces for {@link DynamicConfiguration} definition for a configuration schema.
*
- * @param classDef Class definition.
- * @param schemaField Internal id field from the base schema or one of extensions.
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link DynamicConfiguration} definition for a configuration schema.
*/
- private void addNodeInternalIdMethod(ClassDefinition classDef, Field schemaField) {
- MethodDefinition internalIdMtd = classDef.declareMethod(
- of(PUBLIC),
- schemaField.getName(),
- type(UUID.class)
- );
+ ParameterizedType[] configClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
+ List<ParameterizedType> result = Stream.concat(Stream.of(schemaClass), schemaExtensions.stream())
+ .map(cls -> typeFromJavaClassName(configurationClassName(cls)))
+ .collect(toCollection(ArrayList::new));
- // return this.internalId();
- internalIdMtd.getBody().append(internalIdMtd.getThis().invoke(INTERNAL_ID)).retObject();
+ return result.toArray(new ParameterizedType[0]);
}
/**
- * Implements getter method from {@code VIEW} interface. It returns field value, possibly unboxed or cloned, depending on type.
- *
- * @param classDef Node class definition.
- * @param schemaField Configuration Schema class field.
- * @param getFieldCodeFun Function for creating bytecode to get a field, for example: {@code this.field} or {@code
- * this.field.field}.
- * @param getPolymorphicTypeIdFieldFun Function for creating bytecode to get the field that stores the identifier of the polymorphic
- * configuration instance is needed to add a polymorphicTypeId check, for example: {@code
- * this.typeId} or {@code this.field.typeId}.
- */
- private void addNodeViewMethod(
- ClassDefinition classDef,
- Field schemaField,
- Function<MethodDefinition, BytecodeExpression> getFieldCodeFun,
- @Nullable Function<MethodDefinition, BytecodeExpression> getPolymorphicTypeIdFieldFun
- ) {
- Class<?> schemaFieldType = schemaField.getType();
-
- ParameterizedType returnType;
-
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaFieldType);
-
- // Return type is either corresponding VIEW type or the same type as declared in schema.
- if (isConfigValue(schemaField)) {
- returnType = typeFromJavaClassName(schemaClassInfo.viewClassName);
- } else if (isNamedConfigValue(schemaField)) {
- returnType = type(NamedListView.class);
- } else {
- returnType = type(schemaFieldType);
- }
-
- String fieldName = schemaField.getName();
-
- MethodDefinition viewMtd = classDef.declareMethod(
- of(PUBLIC),
- fieldName,
- returnType
- );
-
- BytecodeBlock bytecodeBlock = new BytecodeBlock();
-
- // result = this.field; OR this.field.field.
- bytecodeBlock.append(getFieldCodeFun.apply(viewMtd));
-
- if (schemaFieldType.isPrimitive()) {
- // result = Box.boxValue(result); // Unboxing.
- bytecodeBlock.invokeVirtual(
- box(schemaFieldType),
- schemaFieldType.getSimpleName() + "Value",
- schemaFieldType
- );
- } else if (schemaFieldType.isArray()) {
- // result = result.clone();
- bytecodeBlock.invokeVirtual(schemaFieldType, "clone", Object.class).checkCast(schemaFieldType);
- } else if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
- // result = result.specificNode();
- bytecodeBlock.invokeVirtual(SPECIFIC_NODE_MTD);
- }
-
- // return result;
- bytecodeBlock.ret(schemaFieldType);
-
- if (getPolymorphicTypeIdFieldFun != null) {
- assert isPolymorphicConfigInstance(schemaField.getDeclaringClass()) : schemaField;
-
- // tmpVar = this.typeId; OR this.field.typeId.
- BytecodeExpression getPolymorphicTypeIdFieldValue = getPolymorphicTypeIdFieldFun.apply(viewMtd);
- String polymorphicInstanceId = polymorphicInstanceId(schemaField.getDeclaringClass());
-
- // if (!"first".equals(tmpVar)) throw Ex;
- // else return value;
- viewMtd.getBody().append(
- new IfStatement()
- .condition(not(constantString(polymorphicInstanceId).invoke(STRING_EQUALS_MTD, getPolymorphicTypeIdFieldValue)))
- .ifTrue(throwException(ConfigurationWrongPolymorphicTypeIdException.class, getPolymorphicTypeIdFieldValue))
- .ifFalse(bytecodeBlock)
- );
- } else {
- viewMtd.getBody().append(bytecodeBlock);
- }
- }
-
- /**
- * Implements changer method from {@code CHANGE} interface.
- *
- * @param classDef Node class definition.
- * @param schemaField Configuration schema class field.
- * @return Definition of change method.
- */
- private MethodDefinition addNodeChangeMethod(
- ClassDefinition classDef,
- Field schemaField,
- Function<MethodDefinition, BytecodeExpression> getFieldCodeFun,
- BiFunction<MethodDefinition, BytecodeExpression, BytecodeExpression> setFieldCodeFun,
- @Nullable Function<MethodDefinition, BytecodeExpression> getPolymorphicTypeIdFieldFun
- ) {
- Class<?> schemaFieldType = schemaField.getType();
-
- MethodDefinition changeMtd = classDef.declareMethod(
- of(PUBLIC),
- changeMethodName(schemaField.getName()),
- classDef.getType(),
- // Change argument type is a Consumer for all inner or named fields.
- arg("change", isValue(schemaField) ? type(schemaFieldType) : type(Consumer.class))
- );
-
- // var change;
- BytecodeExpression changeVar = changeMtd.getScope().getVariable("change");
-
- BytecodeBlock bytecodeBlock = new BytecodeBlock();
-
- if (!schemaFieldType.isPrimitive()) {
- // Objects.requireNonNull(newValue, "change");
- bytecodeBlock.append(invokeStatic(REQUIRE_NON_NULL, changeVar, constantString("change")));
- }
-
- if (isValue(schemaField)) {
- BytecodeExpression newValue;
-
- if (schemaFieldType.isPrimitive()) {
- ParameterizedType type = type(box(schemaFieldType));
-
- // newValue = Box.valueOf(newValue); // Boxing.
- newValue = invokeStatic(type, "valueOf", type, singleton(changeVar));
- } else if (schemaFieldType.isArray()) {
- // newValue = newValue.clone();
- newValue = changeVar.invoke("clone", Object.class).cast(schemaFieldType);
- } else {
- newValue = changeVar;
- }
-
- // this.field = newValue;
- bytecodeBlock.append(setFieldCodeFun.apply(changeMtd, newValue));
- } else {
- BytecodeExpression newValue;
-
- if (isConfigValue(schemaField)) {
- // newValue = (this.field == null) ? new ValueNode() : (ValueNode)this.field.copy();
- newValue = newOrCopyNodeField(schemaField, getFieldCodeFun.apply(changeMtd));
- } else {
- assert isNamedConfigValue(schemaField) : schemaField;
-
- // newValue = (ValueNode)this.field.copy();
- newValue = copyNodeField(schemaField, getFieldCodeFun.apply(changeMtd));
- }
-
- // this.field = newValue;
- bytecodeBlock.append(setFieldCodeFun.apply(changeMtd, newValue));
-
- // this.field;
- BytecodeExpression getFieldCode = getFieldCodeFun.apply(changeMtd);
-
- if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
- // this.field.specificNode();
- getFieldCode = getFieldCode.invoke(SPECIFIC_NODE_MTD);
- }
-
- // change.accept(this.field); OR change.accept(this.field.specificNode());
- bytecodeBlock.append(changeVar.invoke(ACCEPT, getFieldCode));
- }
-
- // return this;
- bytecodeBlock.append(changeMtd.getThis()).retObject();
-
- if (getPolymorphicTypeIdFieldFun != null) {
- assert isPolymorphicConfigInstance(schemaField.getDeclaringClass()) : schemaField;
-
- // tmpVar = this.typeId; OR this.field.typeId.
- BytecodeExpression getPolymorphicTypeIdFieldValue = getPolymorphicTypeIdFieldFun.apply(changeMtd);
- String polymorphicInstanceId = polymorphicInstanceId(schemaField.getDeclaringClass());
-
- // if (!"first".equals(tmpVar)) throw Ex;
- // else change_value;
- changeMtd.getBody().append(
- new IfStatement()
- .condition(not(constantString(polymorphicInstanceId).invoke(STRING_EQUALS_MTD, getPolymorphicTypeIdFieldValue)))
- .ifTrue(throwException(ConfigurationWrongPolymorphicTypeIdException.class, getPolymorphicTypeIdFieldValue))
- .ifFalse(bytecodeBlock)
- );
- } else {
- changeMtd.getBody().append(bytecodeBlock);
- }
-
- return changeMtd;
- }
-
- /**
- * Implements changer bridge method from {@code CHANGE} interface.
- *
- * @param classDef Node class definition.
- * @param changeClassName Class name for the CHANGE class.
- * @param changeMtd Definition of change method.
- */
- private static void addNodeChangeBridgeMethod(
- ClassDefinition classDef,
- String changeClassName,
- MethodDefinition changeMtd
- ) {
- MethodDefinition bridgeMtd = classDef.declareMethod(
- of(PUBLIC, SYNTHETIC, BRIDGE),
- changeMtd.getName(),
- typeFromJavaClassName(changeClassName),
- changeMtd.getParameters()
- );
-
- Variable changeVar = bridgeMtd.getScope().getVariable("change");
-
- // this.change*(change);
- BytecodeExpression invokeChangeMtd = bridgeMtd.getThis().invoke(changeMtd, List.of(changeVar));
-
- // return this.change*(change);
- bridgeMtd.getBody().append(invokeChangeMtd).retObject();
- }
-
- /**
- * Implements {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} method.
- *
- * @param classDef Class definition.
- * @param schemaClass Configuration schema class.
- * @param fieldDefs Definitions for all fields in {@code schemaFields}.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private static void addNodeTraverseChildrenMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Map<String, FieldDefinition> fieldDefs,
- List<Field> schemaFields,
- Collection<Field> internalFields,
- Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
- @Nullable FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition traverseChildrenMtd = classDef.declareMethod(
- of(PUBLIC),
- "traverseChildren",
- type(void.class),
- arg("visitor", type(ConfigurationVisitor.class)),
- arg("includeInternal", type(boolean.class))
- ).addException(NoSuchElementException.class);
-
- BytecodeBlock mtdBody = traverseChildrenMtd.getBody();
-
- // invokeVisit for public (common in case polymorphic config) fields.
- for (Field schemaField : schemaFields) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- mtdBody.append(
- invokeVisit(traverseChildrenMtd, schemaField, fieldDefs.get(schemaField.getName())).pop()
- );
- }
-
- if (!internalFields.isEmpty()) {
- BytecodeBlock includeInternalBlock = new BytecodeBlock();
-
- for (Field internalField : internalFields) {
- includeInternalBlock.append(
- invokeVisit(traverseChildrenMtd, internalField, fieldDefs.get(internalField.getName())).pop()
- );
- }
-
- // if (includeInternal) invokeVisit for internal fields.
- mtdBody.append(
- new IfStatement()
- .condition(traverseChildrenMtd.getScope().getVariable("includeInternal"))
- .ifTrue(includeInternalBlock)
- );
- } else if (!polymorphicFieldsByExtension.isEmpty()) {
- assert polymorphicTypeIdFieldDef != null : schemaClass.getName();
- assert schemaFields.stream().anyMatch(ConfigurationUtil::isPolymorphicId) :
- "Missing field with @PolymorphicId in " + schemaClass.getName();
-
- // Create switch by polymorphicTypeIdField.
- StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(traverseChildrenMtd, polymorphicTypeIdFieldDef);
-
- for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
- BytecodeBlock codeBlock = new BytecodeBlock();
-
- for (Field polymorphicField : e.getValue()) {
- String fieldName = fieldName(polymorphicField);
-
- // invokeVisit for specific polymorphic config fields.
- codeBlock.append(
- invokeVisit(traverseChildrenMtd, polymorphicField, fieldDefs.get(fieldName)).pop()
- );
- }
-
- switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), codeBlock);
- }
-
- // if (polymorphicTypeIdField != null) switch_by_polymorphicTypeIdField
- mtdBody.append(
- new IfStatement()
- .condition(isNotNull(getThisFieldCode(traverseChildrenMtd, polymorphicTypeIdFieldDef)))
- .ifTrue(switchBuilderTypeId.build())
- );
- }
-
- mtdBody.ret();
- }
-
- /**
- * Implements {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} method.
- *
- * @param classDef Class definition.
- * @param fieldDefs Definitions for all fields in {@code schemaFields}.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private static void addNodeTraverseChildMethod(
- ClassDefinition classDef,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
- @Nullable FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition traverseChildMtd = classDef.declareMethod(
- of(PUBLIC),
- "traverseChild",
- type(Object.class),
- arg("key", type(String.class)),
- arg("visitor", type(ConfigurationVisitor.class)),
- arg("includeInternal", type(boolean.class))
- ).addException(NoSuchElementException.class);
-
- Variable keyVar = traverseChildMtd.getScope().getVariable("key");
-
- // Create switch for public (common in case polymorphic config) fields only.
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(traverseChildMtd.getScope()).expression(keyVar);
-
- for (Field schemaField : schemaFields) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- String fieldName = fieldName(schemaField);
-
- switchBuilder.addCase(
- fieldName,
- invokeVisit(traverseChildMtd, schemaField, fieldDefs.get(fieldName)).retObject()
- );
- }
-
- if (!internalFields.isEmpty()) {
- // Create switch for public + internal fields.
- StringSwitchBuilder switchBuilderAllFields = new StringSwitchBuilder(traverseChildMtd.getScope())
- .expression(keyVar)
- .defaultCase(throwException(NoSuchElementException.class, keyVar));
-
- for (Field schemaField : concat(schemaFields, internalFields)) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- String fieldName = fieldName(schemaField);
-
- switchBuilderAllFields.addCase(
- fieldName,
- invokeVisit(traverseChildMtd, schemaField, fieldDefs.get(fieldName)).retObject()
- );
- }
-
- // if (includeInternal) switch_by_all_fields
- // else switch_only_public_fields
- traverseChildMtd.getBody().append(
- new IfStatement()
- .condition(traverseChildMtd.getScope().getVariable("includeInternal"))
- .ifTrue(switchBuilderAllFields.build())
- .ifFalse(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
- );
- } else if (!polymorphicFieldsByExtension.isEmpty()) {
- assert polymorphicTypeIdFieldDef != null : classDef.getName();
-
- // Create switch by polymorphicTypeIdField.
- StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(traverseChildMtd, polymorphicTypeIdFieldDef);
-
- for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
- // Create switch for specific polymorphic instance.
- StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(traverseChildMtd.getScope())
- .expression(keyVar)
- .defaultCase(throwException(NoSuchElementException.class, keyVar));
-
- for (Field polymorphicField : e.getValue()) {
- String fieldName = fieldName(polymorphicField);
-
- switchBuilderPolymorphicExtension.addCase(
- polymorphicField.getName(),
- invokeVisit(traverseChildMtd, polymorphicField, fieldDefs.get(fieldName)).retObject()
- );
- }
-
- switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), switchBuilderPolymorphicExtension.build());
- }
-
- // switch_by_common_fields
- // switch_by_polymorphicTypeIdField
- // switch_by_polymorphic_0_fields
- // switch_by_polymorphic_1_fields
- // ...
- traverseChildMtd.getBody()
- .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
- .append(switchBuilderTypeId.build());
- } else {
- traverseChildMtd.getBody()
- .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build());
- }
- }
-
- /**
- * Creates bytecode block that invokes one of {@link ConfigurationVisitor}'s methods.
- *
- * @param mtd Method definition, either {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} or {@link
- * InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} defined in {@code *Node} class.
- * @param schemaField Configuration Schema field to visit.
- * @param fieldDef Field definition from current class.
- * @return Bytecode block that invokes "visit*" method.
- */
- private static BytecodeBlock invokeVisit(MethodDefinition mtd, Field schemaField, FieldDefinition fieldDef) {
- Method visitMethod;
-
- if (isValue(schemaField) || isPolymorphicId(schemaField)) {
- visitMethod = VISIT_LEAF;
- } else if (isConfigValue(schemaField)) {
- visitMethod = VISIT_INNER;
- } else {
- visitMethod = VISIT_NAMED;
- }
-
- return new BytecodeBlock().append(mtd.getScope().getVariable("visitor").invoke(
- visitMethod,
- constantString(schemaField.getName()),
- mtd.getThis().getField(fieldDef)
- ));
- }
-
- /**
- * Implements {@link ConstructableTreeNode#construct(String, ConfigurationSource, boolean)} method.
- *
- * @param classDef Class definition.
- * @param fieldDefs Definitions for all fields in {@code schemaFields}.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- * @param changePolymorphicTypeIdMtd Method for changing the type of polymorphic configuration.
- */
- private void addNodeConstructMethod(
- ClassDefinition classDef,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
- @Nullable FieldDefinition polymorphicTypeIdFieldDef,
- @Nullable MethodDefinition changePolymorphicTypeIdMtd
- ) {
- MethodDefinition constructMtd = classDef.declareMethod(
- of(PUBLIC),
- CONSTRUCT_MTD_NAME,
- type(void.class),
- arg("key", type(String.class)),
- arg("src", type(ConfigurationSource.class)),
- arg("includeInternal", type(boolean.class))
- ).addException(NoSuchElementException.class);
-
- Variable keyVar = constructMtd.getScope().getVariable("key");
- Variable srcVar = constructMtd.getScope().getVariable("src");
-
- // Create switch for public (common in case polymorphic config) fields only.
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructMtd.getScope()).expression(keyVar);
-
- for (Field schemaField : schemaFields) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- String fieldName = fieldName(schemaField);
- FieldDefinition fieldDef = fieldDefs.get(fieldName);
-
- if (isPolymorphicId(schemaField)) {
- // src == null ? null : src.unwrap(FieldType.class);
- BytecodeExpression getTypeIdFromSrcVar = inlineIf(
- isNull(srcVar),
- constantNull(fieldDef.getType()),
- srcVar.invoke(UNWRAP, constantClass(fieldDef.getType())).cast(fieldDef.getType())
- );
-
- // this.changePolymorphicTypeId(src == null ? null : src.unwrap(FieldType.class));
- switchBuilder.addCase(
- fieldName,
- new BytecodeBlock()
- .append(constructMtd.getThis())
- .append(getTypeIdFromSrcVar)
- .invokeVirtual(changePolymorphicTypeIdMtd)
- .ret()
- );
- } else {
- switchBuilder.addCase(
- fieldName,
- treatSourceForConstruct(constructMtd, schemaField, fieldDef).ret()
- );
- }
- }
-
- if (!internalFields.isEmpty()) {
- // Create switch for public + internal fields.
- StringSwitchBuilder switchBuilderAllFields = new StringSwitchBuilder(constructMtd.getScope())
- .expression(keyVar)
- .defaultCase(throwException(NoSuchElementException.class, keyVar));
-
- for (Field schemaField : concat(schemaFields, internalFields)) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- String fieldName = fieldName(schemaField);
-
- switchBuilderAllFields.addCase(
- fieldName,
- treatSourceForConstruct(constructMtd, schemaField, fieldDefs.get(fieldName)).ret()
- );
- }
-
- // if (includeInternal) switch_by_all_fields
- // else switch_only_public_fields
- constructMtd.getBody().append(
- new IfStatement().condition(constructMtd.getScope().getVariable("includeInternal"))
- .ifTrue(switchBuilderAllFields.build())
- .ifFalse(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
- ).ret();
- } else if (!polymorphicFieldsByExtension.isEmpty()) {
- assert polymorphicTypeIdFieldDef != null : classDef.getName();
-
- // Create switch by polymorphicTypeIdField.
- StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(constructMtd, polymorphicTypeIdFieldDef);
-
- for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
- // Create switch for specific polymorphic instance.
- StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(constructMtd.getScope())
- .expression(keyVar)
- .defaultCase(throwException(NoSuchElementException.class, keyVar));
-
- for (Field polymorphicField : e.getValue()) {
- String fieldName = fieldName(polymorphicField);
- FieldDefinition fieldDef = fieldDefs.get(fieldName);
-
- switchBuilderPolymorphicExtension.addCase(
- polymorphicField.getName(),
- treatSourceForConstruct(constructMtd, polymorphicField, fieldDef).ret()
- );
- }
-
- switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), switchBuilderPolymorphicExtension.build());
- }
-
- // switch_by_common_fields
- // switch_by_polymorphicTypeIdField
- // switch_by_polymorphic_0_fields
- // switch_by_polymorphic_1_fields
- // ...
- constructMtd.getBody()
- .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
- .append(switchBuilderTypeId.build())
- .ret();
- } else {
- constructMtd.getBody()
- .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
- .ret();
- }
- }
-
- /**
- * Implements {@link InnerNode#constructDefault(String)} method.
- *
- * @param schemaClass Configuration schema class.
- * @param classDef Class definition.
- * @param specFields Field definitions for the schema and its extensions: {@code _spec#}.
- * @param fieldDefs Definitions for all fields in {@code schemaFields}.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private static void addNodeConstructDefaultMethod(
- Class<?> schemaClass,
- ClassDefinition classDef,
- Map<Class<?>, FieldDefinition> specFields,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
- @Nullable FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition constructDfltMtd = classDef.declareMethod(
- of(PUBLIC),
- "constructDefault",
- type(void.class),
- arg("key", String.class)
- ).addException(NoSuchElementException.class);
-
- Variable keyVar = constructDfltMtd.getScope().getVariable("key");
-
- // Create switch for public (common in case polymorphic config) + internal fields.
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructDfltMtd.getScope()).expression(keyVar);
-
- for (Field schemaField : concat(schemaFields, internalFields)) {
- if (isInjectedName(schemaField)) {
- continue;
- }
-
- if (isValue(schemaField) || isPolymorphicId(schemaField)) {
- String fieldName = schemaField.getName();
-
- if (isValue(schemaField) && !hasDefault(schemaField)
- || isPolymorphicId(schemaField) && !schemaField.getAnnotation(PolymorphicId.class).hasDefault()) {
- // return;
- switchBuilder.addCase(fieldName, new BytecodeBlock().ret());
- } else {
- FieldDefinition fieldDef = fieldDefs.get(fieldName);
-
- Class<?> fieldType = schemaField.getDeclaringClass();
-
- FieldDefinition specFieldDef = fieldType.isAnnotationPresent(AbstractConfiguration.class)
- ? specFields.get(schemaClass)
- : specFields.get(fieldType);
-
- // this.field = spec_#.field;
- switchBuilder.addCase(
- fieldName,
- addNodeConstructDefault(constructDfltMtd, schemaField, fieldDef, specFieldDef).ret()
- );
- }
- }
- }
-
- if (!polymorphicFieldsByExtension.isEmpty()) {
- // Create switch by polymorphicTypeIdField.
- StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(constructDfltMtd, polymorphicTypeIdFieldDef);
-
- for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
- // Create switch for specific polymorphic instance.
- StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(constructDfltMtd.getScope())
- .expression(keyVar)
- .defaultCase(throwException(NoSuchElementException.class, keyVar));
-
- for (Field polymorphicField : e.getValue()) {
- if (isValue(polymorphicField)) {
- if (!hasDefault(polymorphicField)) {
- // return;
- switchBuilderPolymorphicExtension.addCase(polymorphicField.getName(), new BytecodeBlock().ret());
- } else {
- FieldDefinition fieldDef = fieldDefs.get(fieldName(polymorphicField));
- FieldDefinition specFieldDef = specFields.get(polymorphicField.getDeclaringClass());
-
- // this.field = spec_#.field;
- switchBuilderPolymorphicExtension.addCase(
- polymorphicField.getName(),
- addNodeConstructDefault(constructDfltMtd, polymorphicField, fieldDef, specFieldDef).ret()
- );
- }
- }
- }
-
- switchBuilderTypeId.addCase(
- polymorphicInstanceId(e.getKey()),
- switchBuilderPolymorphicExtension.build()
- );
- }
-
- // switch_by_common_fields
- // switch_by_polymorphicTypeIdField
- // switch_by_polymorphic_0_fields
- // switch_by_polymorphic_1_fields
- // ...
- constructDfltMtd.getBody()
- .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
- .append(switchBuilderTypeId.build())
- .ret();
- } else {
- constructDfltMtd.getBody()
- .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
- .ret();
- }
- }
-
- /**
- * Copies field into itself or instantiates it if the field is null. Code like: {@code this.field == null ? new ValueNode() :
- * (ValueNode)this.field.copy();}.
- *
- * @param schemaField Configuration schema class field.
- * @param getFieldCode Bytecode of getting the field, for example: {@code this.field} or {@code this.field.field};
- * @return Bytecode expression.
- */
- private BytecodeExpression newOrCopyNodeField(Field schemaField, BytecodeExpression getFieldCode) {
- ParameterizedType nodeType = typeFromJavaClassName(schemasInfo.get(schemaField.getType()).nodeClassName);
-
- // this.field == null ? new ValueNode() : (ValueNode)this.field.copy();
- return inlineIf(
- isNull(getFieldCode),
- newInstance(nodeType),
- copyNodeField(schemaField, getFieldCode)
- );
- }
-
- /**
- * Copies field into itself. Code like: {@code (ValueNode)this.field.copy();}.
- *
- * @param schemaField Configuration schema class field.
- * @param getFieldCode Bytecode of getting the field, for example: {@code this.field} or {@code this.field.field};
- * @return Bytecode expression.
- */
- private BytecodeExpression copyNodeField(Field schemaField, BytecodeExpression getFieldCode) {
- ParameterizedType nodeType = isNamedConfigValue(schemaField)
- ? type(NamedListNode.class) : typeFromJavaClassName(schemasInfo.get(schemaField.getType()).nodeClassName);
-
- // (ValueNode)this.field.copy();
- return getFieldCode.invoke(COPY).cast(nodeType);
- }
-
- /**
- * Creates {@code *Node::new} lambda expression with {@link Supplier} type.
- *
- * @param nodeClassName Name of the {@code *Node} class.
- * @return InvokeDynamic bytecode expression.
- */
- @NotNull
- private static BytecodeExpression newNamedListElementLambda(String nodeClassName) {
- return invokeDynamic(
- LAMBDA_METAFACTORY,
- asList(
- getMethodType(getType(Object.class)),
- new Handle(
- H_NEWINVOKESPECIAL,
- internalName(nodeClassName),
- "<init>",
- getMethodDescriptor(Type.VOID_TYPE),
- false
- ),
- getMethodType(typeFromJavaClassName(nodeClassName).getAsmType())
- ),
- "get",
- methodType(Supplier.class)
- );
- }
-
- /**
- * Construct a {@link DynamicConfiguration} definition for a configuration schema.
- *
- * @param schemaClass Configuration schema class.
- * @param internalExtensions Internal extensions of the configuration schema.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFields Fields of polymorphic extensions of the configuration schema.
- * @param internalIdField Internal id field or {@code null} if it's not present.
- * @return Constructed {@link DynamicConfiguration} definition for the configuration schema.
- */
- private ClassDefinition createCfgImplClass(
- Class<?> schemaClass,
- Set<Class<?>> internalExtensions,
- Set<Class<?>> polymorphicExtensions,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Collection<Field> polymorphicFields,
- @Nullable Field internalIdField
- ) {
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
-
- // Configuration impl class definition.
- ClassDefinition classDef = new ClassDefinition(
- of(PUBLIC, FINAL),
- internalName(schemaClassInfo.cfgImplClassName),
- type(DynamicConfiguration.class),
- configClassInterfaces(schemaClass, internalExtensions)
- );
-
- // Fields.
- Map<String, FieldDefinition> fieldDefs = new HashMap<>();
-
- // To store the id of the polymorphic configuration instance.
- FieldDefinition polymorphicTypeIdFieldDef = null;
-
- for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
- String fieldName = fieldName(schemaField);
-
- FieldDefinition fieldDef = addConfigurationImplField(classDef, schemaField, fieldName);
-
- fieldDefs.put(fieldName, fieldDef);
-
- if (isPolymorphicId(schemaField)) {
- polymorphicTypeIdFieldDef = fieldDef;
- }
- }
-
- if (internalIdField != null) {
- // Internal id dynamic property is stored as a regular field.
- String fieldName = internalIdField.getName();
-
- FieldDefinition fieldDef = addConfigurationImplField(classDef, internalIdField, fieldName);
-
- fieldDefs.put(fieldName, fieldDef);
- }
-
- FieldDefinition internalConfigTypesFieldDef = null;
-
- if (!internalExtensions.isEmpty()) {
- internalConfigTypesFieldDef = classDef.declareField(
- of(PRIVATE, FINAL),
- INTERNAL_CONFIG_TYPES_FIELD_NAME,
- Class[].class
- );
- }
-
- // Constructor
- addConfigurationImplConstructor(
- classDef,
- schemaClass,
- internalExtensions,
- fieldDefs,
- schemaFields,
- internalFields,
- polymorphicFields,
- internalIdField,
- internalConfigTypesFieldDef
- );
-
- // org.apache.ignite.internal.configuration.DynamicProperty#directProxy
- addDirectProxyMethod(schemaClassInfo, classDef);
-
- // Getter for the internal id.
- if (internalIdField != null) {
- addConfigurationImplGetMethod(classDef, internalIdField, fieldDefs.get(internalIdField.getName()));
- }
-
- for (Field schemaField : concat(schemaFields, internalFields)) {
- addConfigurationImplGetMethod(classDef, schemaField, fieldDefs.get(fieldName(schemaField)));
- }
-
- // org.apache.ignite.internal.configuration.DynamicConfiguration#configType
- addCfgImplConfigTypeMethod(classDef, typeFromJavaClassName(schemaClassInfo.cfgClassName));
-
- if (internalConfigTypesFieldDef != null) {
- addCfgImplInternalConfigTypesMethod(classDef, internalConfigTypesFieldDef);
- }
-
- if (!polymorphicExtensions.isEmpty()) {
- addCfgSpecificConfigTreeMethod(classDef, schemaClass, polymorphicExtensions, polymorphicTypeIdFieldDef);
-
- addCfgRemoveMembersMethod(
- classDef,
- schemaClass,
- polymorphicExtensions,
- fieldDefs,
- polymorphicFields,
- polymorphicTypeIdFieldDef
- );
-
- addCfgAddMembersMethod(
- classDef,
- schemaClass,
- polymorphicExtensions,
- fieldDefs,
- polymorphicFields,
- polymorphicTypeIdFieldDef
- );
-
- addCfgImplPolymorphicInstanceConfigTypeMethod(
- classDef,
- schemaClass,
- polymorphicExtensions,
- polymorphicTypeIdFieldDef
- );
- }
-
- return classDef;
- }
-
- /**
- * Declares field that corresponds to configuration value. Depending on the schema, 3 options possible:
- * <ul>
- * <li>
- * {@code @Value public type fieldName}<br/>becomes<br/>
- * {@code public DynamicProperty fieldName}
- * </li>
- * <li>
- * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
- * {@code public MyConfiguration fieldName}
- * </li>
- * <li>
- * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
- * {@code public NamedListConfiguration fieldName}
- * </li>
- * <li>
- * {@code @PolymorphicId public String fieldName}<br/>becomes<br/>
- * {@code public String fieldName}
- * </li>
- * </ul>
- *
- * @param classDef Configuration impl class definition.
- * @param schemaField Configuration Schema class field.
- * @param fieldName Field name, if {@code null} will be used {@link Field#getName}.
- * @return Declared field definition.
- */
- private FieldDefinition addConfigurationImplField(
- ClassDefinition classDef,
- Field schemaField,
- String fieldName
- ) {
- ParameterizedType fieldType;
-
- if (isConfigValue(schemaField)) {
- fieldType = typeFromJavaClassName(schemasInfo.get(schemaField.getType()).cfgImplClassName);
- } else if (isNamedConfigValue(schemaField)) {
- fieldType = type(NamedListConfiguration.class);
- } else {
- fieldType = type(DynamicProperty.class);
- }
-
- return classDef.declareField(of(PUBLIC), fieldName, fieldType);
- }
-
- /**
- * Implements default constructor for the configuration class. It initializes all fields and adds them to members collection.
- *
- * @param classDef Configuration impl class definition.
- * @param schemaClass Configuration schema class.
- * @param internalExtensions Internal extensions of the configuration schema.
- * @param fieldDefs Field definitions for all fields of configuration impl class.
- * @param schemaFields Fields of the schema class.
- * @param internalFields Fields of internal extensions of the configuration schema.
- * @param polymorphicFields Fields of polymorphic extensions of the configuration schema.
- * @param internalIdField Internal id field or {@code null} if it's not present.
- * @param internalConfigTypesFieldDef Field definition for {@link DynamicConfiguration#internalConfigTypes},
- * {@code null} if there are no internal extensions.
- */
- private void addConfigurationImplConstructor(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> internalExtensions,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> schemaFields,
- Collection<Field> internalFields,
- Collection<Field> polymorphicFields,
- @Nullable Field internalIdField,
- @Nullable FieldDefinition internalConfigTypesFieldDef
- ) {
- MethodDefinition ctor = classDef.declareConstructor(
- of(PUBLIC),
- arg("prefix", List.class),
- arg("key", String.class),
- arg("rootKey", RootKey.class),
- arg("changer", DynamicConfigurationChanger.class),
- arg("listenOnly", boolean.class)
- );
-
- Variable rootKeyVar = ctor.getScope().getVariable("rootKey");
- Variable changerVar = ctor.getScope().getVariable("changer");
- Variable listenOnlyVar = ctor.getScope().getVariable("listenOnly");
-
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
-
- Variable thisVar = ctor.getThis();
-
- BytecodeBlock ctorBody = ctor.getBody()
- .append(thisVar)
- .append(ctor.getScope().getVariable("prefix"))
- .append(ctor.getScope().getVariable("key"))
- .append(rootKeyVar)
- .append(changerVar)
- .append(listenOnlyVar)
- .invokeConstructor(DYNAMIC_CONFIGURATION_CTOR);
-
- BytecodeExpression thisKeysVar = thisVar.getField("keys", List.class);
-
- // Wrap object into list to reuse the loop below.
- List<Field> internalIdFieldAsList = internalIdField == null ? emptyList() : List.of(internalIdField);
-
- int newIdx = 0;
- for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields, internalIdFieldAsList)) {
- String fieldName = schemaField.getName();
-
- BytecodeExpression newValue;
-
- if (isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField)) {
- // A field with @InjectedName is special (auxiliary), it is not stored in storages as a regular field, and therefore there
- // is no direct access to it. It is stored in the InnerNode and does not participate in its traversal, so in order to get
- // it we need to get the InnerNode, and only then the value of this field.
-
- // newValue = new DynamicProperty(this.keys, fieldName, rootKey, changer, listenOnly, readOnly);
- newValue = newInstance(
- DynamicProperty.class,
- thisKeysVar,
- constantString(isInjectedName(schemaField) ? InnerNode.INJECTED_NAME
- : isInternalId(schemaField) ? InnerNode.INTERNAL_ID : schemaField.getName()),
- rootKeyVar,
- changerVar,
- listenOnlyVar,
- constantBoolean(isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField))
- );
- } else {
- SchemaClassesInfo fieldInfo = schemasInfo.get(schemaField.getType());
-
- ParameterizedType cfgImplParameterizedType = typeFromJavaClassName(fieldInfo.cfgImplClassName);
-
- if (isConfigValue(schemaField)) {
- // newValue = new MyConfigurationImpl(super.keys, fieldName, rootKey, changer, listenOnly);
- newValue = newInstance(
- cfgImplParameterizedType,
- thisKeysVar,
- constantString(fieldName),
- rootKeyVar,
- changerVar,
- listenOnlyVar
- );
- } else {
- // We have to create method "$new$<idx>" to reference it in lambda expression. That's the way it
- // works, it'll invoke constructor with all 5 arguments, not just 2 as in BiFunction.
- MethodDefinition newMtd = classDef.declareMethod(
- of(PRIVATE, STATIC, SYNTHETIC),
- "$new$" + newIdx++,
- typeFromJavaClassName(fieldInfo.cfgClassName),
- arg("rootKey", RootKey.class),
- arg("changer", DynamicConfigurationChanger.class),
- arg("listenOnly", boolean.class),
- arg("prefix", List.class),
- arg("key", String.class)
- );
-
- // newValue = new NamedListConfiguration(this.keys, fieldName, rootKey, changer, listenOnly,
- // (p, k) -> new ValueConfigurationImpl(p, k, rootKey, changer, listenOnly),
- // (p, c) -> new ValueDirectProxy(p, c),
- // new ValueConfigurationImpl(this.keys, "any", rootKey, changer, true)
- // );
- newValue = newInstance(
- NamedListConfiguration.class,
- thisKeysVar,
- constantString(fieldName),
- rootKeyVar,
- changerVar,
- listenOnlyVar,
- invokeDynamic(
- LAMBDA_METAFACTORY,
- asList(
- getMethodType(getType(Object.class), getType(Object.class), getType(Object.class)),
- new Handle(
- Opcodes.H_INVOKESTATIC,
- internalName(schemaClassInfo.cfgImplClassName),
- newMtd.getName(),
- newMtd.getMethodDescriptor(),
- false
- ),
- getMethodType(
- typeFromJavaClassName(fieldInfo.cfgClassName).getAsmType(),
- getType(List.class),
- getType(String.class)
- )
- ),
- "apply",
- BiFunction.class,
- rootKeyVar,
- changerVar,
- listenOnlyVar
- ),
- newDirectProxyLambda(fieldInfo),
- newInstance(
- cfgImplParameterizedType,
- thisKeysVar,
- constantString("any"),
- rootKeyVar,
- changerVar,
- constantBoolean(true)
- ).cast(ConfigurationProperty.class)
- );
-
- newMtd.getBody()
- .append(newInstance(
- cfgImplParameterizedType,
- newMtd.getScope().getVariable("prefix"),
- newMtd.getScope().getVariable("key"),
- newMtd.getScope().getVariable("rootKey"),
- newMtd.getScope().getVariable("changer"),
- newMtd.getScope().getVariable("listenOnly")
- ))
- .retObject();
- }
- }
-
- FieldDefinition fieldDef = fieldDefs.get(fieldName(schemaField));
-
- // this.field = newValue;
- ctorBody.append(thisVar.setField(fieldDef, newValue));
-
- if (!isPolymorphicConfigInstance(schemaField.getDeclaringClass()) && !isInternalId(schemaField)) {
- // add(this.field);
- ctorBody.append(thisVar.invoke(DYNAMIC_CONFIGURATION_ADD_MTD, thisVar.getField(fieldDef)));
- }
- }
-
- if (internalConfigTypesFieldDef != null) {
- assert !internalExtensions.isEmpty() : classDef;
-
- // Class[] tmp;
- Variable tmpVar = ctor.getScope().createTempVariable(Class[].class);
-
- BytecodeBlock initInternalConfigTypesField = new BytecodeBlock();
-
- // tmp = new Class[size];
- initInternalConfigTypesField.append(tmpVar.set(newArray(type(Class[].class), internalExtensions.size())));
-
- int i = 0;
-
- for (Class<?> extension : internalExtensions) {
- // tmp[i] = InternalTableConfiguration.class;
- initInternalConfigTypesField.append(set(
- tmpVar,
- constantInt(i++),
- constantClass(typeFromJavaClassName(configurationClassName(extension)))
- ));
- }
-
- // this._internalConfigTypes = tmp;
- initInternalConfigTypesField.append(setThisFieldCode(ctor, tmpVar, internalConfigTypesFieldDef));
-
- ctorBody.append(initInternalConfigTypesField);
- }
-
- ctorBody.ret();
- }
-
- /**
- * Generates {@link ConfigurationNode#directProxy()} method that returns new instance every time.
- *
- * @param schemaClassInfo Schema class info.
- * @param classDef Class definition.
- */
- private void addDirectProxyMethod(
- SchemaClassesInfo schemaClassInfo,
- ClassDefinition classDef
- ) {
- MethodDefinition methodDef = classDef.declareMethod(
- of(PUBLIC), "directProxy", type(DirectPropertyProxy.class)
- );
-
- methodDef.getBody().append(newInstance(
- typeFromJavaClassName(schemaClassInfo.directProxyClassName),
- methodDef.getThis().invoke("keyPath", List.class),
- methodDef.getThis().getField("changer", DynamicConfigurationChanger.class)
- ));
-
- methodDef.getBody().retObject();
- }
-
- /**
- * Implements accessor method in configuration impl class.
- *
- * @param classDef Configuration impl class definition.
- * @param schemaField Configuration Schema class field.
- * @param fieldDefs Field definitions.
- */
- private void addConfigurationImplGetMethod(
- ClassDefinition classDef,
- Field schemaField,
- FieldDefinition... fieldDefs
- ) {
- assert !nullOrEmpty(fieldDefs);
-
- Class<?> schemaFieldType = schemaField.getType();
-
- String fieldName = schemaField.getName();
-
- ParameterizedType returnType;
-
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaFieldType);
-
- if (isConfigValue(schemaField)) {
- returnType = typeFromJavaClassName(schemaClassInfo.cfgClassName);
- } else if (isNamedConfigValue(schemaField)) {
- returnType = type(NamedConfigurationTree.class);
- } else {
- assert isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField)
- || isInternalId(schemaField) : schemaField;
-
- returnType = type(ConfigurationValue.class);
- }
-
- // public ConfigurationProperty fieldName()
- MethodDefinition viewMtd = classDef.declareMethod(
- of(PUBLIC),
- fieldName,
- returnType
- );
-
- // result = this.field;
- BytecodeBlock body = viewMtd.getBody().append(getThisFieldCode(viewMtd, fieldDefs));
-
- if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
- // result = this.field.specificConfigTree();
- body.invokeVirtual(SPECIFIC_CONFIG_TREE_MTD);
- }
-
- // return result;
- body.retObject();
- }
-
- /**
- * Replaces first letter in string with its upper-cased variant.
- *
- * @param name Some string.
- * @return Capitalized version of passed string.
- */
- private static String capitalize(String name) {
- return name.substring(0, 1).toUpperCase() + name.substring(1);
- }
-
- /**
- * Returns internalized version of class name, replacing dots with slashes.
- *
- * @param className Class name (with package).
- * @return Internal class name.
- * @see Type#getInternalName(Class)
- */
- @NotNull
- static String internalName(String className) {
- return className.replace('.', '/');
- }
-
- /**
- * Creates boxed version of the class.
- *
- * @param clazz Maybe primitive class.
- * @return Not primitive class that represents parameter class.
- */
- private static Class<?> box(Class<?> clazz) {
- Class<?> boxed = TypeUtils.boxed(clazz);
-
- return boxed == null ? clazz : boxed;
- }
-
- /**
- * Get interfaces for {@link InnerNode} definition for a configuration schema.
- *
- * @param schemaClass Configuration schema class.
- * @param schemaExtensions Internal extensions of the configuration schema.
- * @return Interfaces for {@link InnerNode} definition for a configuration schema.
- */
- private static ParameterizedType[] nodeClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
- Collection<ParameterizedType> res = new ArrayList<>();
-
- for (Class<?> cls : concat(List.of(schemaClass), schemaExtensions)) {
- res.add(typeFromJavaClassName(viewClassName(cls)));
- res.add(typeFromJavaClassName(changeClassName(cls)));
- }
-
- return res.toArray(ParameterizedType[]::new);
- }
-
- /**
- * Get interfaces for {@link DynamicConfiguration} definition for a configuration schema.
- *
- * @param schemaClass Configuration schema class.
- * @param schemaExtensions Internal extensions of the configuration schema.
- * @return Interfaces for {@link DynamicConfiguration} definition for a configuration schema.
- */
- ParameterizedType[] configClassInterfaces(Class<?> schemaClass, Set<Class<?>> schemaExtensions) {
- List<ParameterizedType> result = Stream.concat(Stream.of(schemaClass), schemaExtensions.stream())
- .map(cls -> typeFromJavaClassName(configurationClassName(cls)))
- .collect(toCollection(ArrayList::new));
-
- return result.toArray(new ParameterizedType[0]);
- }
-
- /**
- * Add {@link DynamicConfiguration#configType} method implementation to the class.
- *
- * <p>It looks like the following code:
- * <pre><code>
- * public Class configType() {
- * return RootConfiguration.class;
- * }
- * </code></pre>
- *
- * @param classDef Class definition.
- * @param clazz Definition of the configuration interface, for example {@code RootConfiguration}.
- */
- private static void addCfgImplConfigTypeMethod(ClassDefinition classDef, ParameterizedType clazz) {
- classDef.declareMethod(of(PUBLIC), "configType", type(Class.class))
- .getBody()
- .append(constantClass(clazz))
- .retObject();
- }
-
- /**
- * Add {@link DynamicConfiguration#internalConfigTypes} method implementation to the class.
- *
- * <p>It looks like the following code:
- * <pre><code>
- * public Class<?>[] internalConfigTypes() {
- * return new Class<?>[]{FirstInternalTableConfiguration.class, SecondInternalTableConfiguration.class};
- * }
- * </code></pre>
- *
- * @param classDef Class definition.
- * @param internalConfigTypesDef Definition of the field in which the interfaces of the internal configuration extensions are stored.
- */
- private static void addCfgImplInternalConfigTypesMethod(ClassDefinition classDef, FieldDefinition internalConfigTypesDef) {
- MethodDefinition internalConfigTypesMtd = classDef.declareMethod(of(PUBLIC), "internalConfigTypes", type(Class[].class));
-
- internalConfigTypesMtd
- .getBody()
- .append(getThisFieldCode(internalConfigTypesMtd, internalConfigTypesDef))
- .retObject();
- }
-
- /**
- * Add {@link DynamicConfiguration#polymorphicInstanceConfigType} method implementation to the class.
- *
- * <p>It looks like the following code:
- * <pre><code>
- * public Class polymorphicInstanceConfigType() {
- * InnerNode val = this.isRemovedFromNamedList() ? this.currentValue() : this.refreshValue();
- * String typeId = val.polymorphicTypeId;
- * switch(typeId) {
- * case "hash":
- * return HashIndexConfiguration.class;
- * case "sorted"
- * return SortedIndexConfiguration.class;
- * default:
- * throw new ConfigurationWrongPolymorphicTypeIdException(typeId);
- * }
- * }
- * </code></pre>
- *
- * @param classDef Class definition.
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private static void addCfgImplPolymorphicInstanceConfigTypeMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition polymorphicInstanceConfigTypeMtd = classDef.declareMethod(
- of(PUBLIC),
- "polymorphicInstanceConfigType",
- type(Class.class)
- );
-
- // String tmpStr;
- Variable tmpStrVar = polymorphicInstanceConfigTypeMtd.getScope().createTempVariable(String.class);
-
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(polymorphicInstanceConfigTypeMtd.getScope())
- .expression(tmpStrVar)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- switchBuilder.addCase(
- polymorphicInstanceId(polymorphicExtension),
- constantClass(typeFromJavaClassName(configurationClassName(polymorphicExtension))).ret()
- );
- }
-
- // ConfigNode
- ParameterizedType nodeType = typeFromJavaClassName(nodeClassName(schemaClass));
-
- // Object tmpObj;
- Variable tmpObjVar = polymorphicInstanceConfigTypeMtd.getScope().createTempVariable(Object.class);
-
- // this;
- Variable thisVar = polymorphicInstanceConfigTypeMtd.getThis();
-
- // tmpObj = this.isRemovedFromNamedList() ? this.currentValue() : this.refreshValue();
- // tmpStr = ((ConfigNode) tmpObj).typeId;
- // switch(tmpStr) ...
- polymorphicInstanceConfigTypeMtd.getBody()
- .append(tmpObjVar.set(inlineIf(
- thisVar.invoke(IS_REMOVED_FROM_NAMED_LIST_MTD),
- thisVar.invoke(CURRENT_VALUE_MTD),
- thisVar.invoke(REFRESH_VALUE_MTD))
- ))
- .append(tmpStrVar.set(tmpObjVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
- .append(switchBuilder.build())
- .ret();
- }
-
- /**
- * Create a {@code *Node} for the polymorphic configuration instance schema.
- *
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtension Polymorphic configuration instance schema (child).
- * @param schemaInnerNodeClassDef {@link InnerNode} definition for the polymorphic configuration schema {@code schemaClass}.
- * @param schemaFields Schema fields of polymorphic configuration {@code schemaClass}.
- * @param polymorphicFields Schema fields of a polymorphic configuration instance {@code polymorphicExtension}.
- * @param internalIdField Internal id field or {@code null} if it's not present.
- */
- private ClassDefinition createPolymorphicExtensionNodeClass(
- Class<?> schemaClass,
- Class<?> polymorphicExtension,
- ClassDefinition schemaInnerNodeClassDef,
- Collection<Field> schemaFields,
- Collection<Field> polymorphicFields,
- @Nullable Field internalIdField
- ) {
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
- SchemaClassesInfo polymorphicExtensionClassInfo = schemasInfo.get(polymorphicExtension);
-
- // Node class definition.
- ClassDefinition classDef = new ClassDefinition(
- of(PUBLIC, FINAL),
- internalName(polymorphicExtensionClassInfo.nodeClassName),
- type(Object.class),
- ArrayUtils.concat(nodeClassInterfaces(polymorphicExtension, Set.of()), type(ConstructableTreeNode.class))
- );
-
- // private final ParentNode this$0;
- FieldDefinition parentInnerNodeFieldDef = classDef.declareField(
- of(PRIVATE, FINAL),
- "this$0",
- typeFromJavaClassName(schemaClassInfo.nodeClassName)
- );
-
- // Constructor.
- MethodDefinition constructorMtd = classDef.declareConstructor(
- of(PUBLIC),
- arg("delegate", typeFromJavaClassName(schemaClassInfo.nodeClassName))
- );
-
- Variable delegateVar = constructorMtd.getScope().getVariable("delegate");
-
- // Constructor body.
- constructorMtd.getBody()
- .append(constructorMtd.getThis())
- .invokeConstructor(Object.class)
- .append(constructorMtd.getThis().setField(
- parentInnerNodeFieldDef,
- delegateVar
- ))
- .ret();
-
- Map<String, FieldDefinition> fieldDefs = schemaInnerNodeClassDef.getFields().stream()
- .collect(toMap(FieldDefinition::getName, identity()));
-
- // Creates method to get the internal id. Almost the same as regular view, but with method invocation instead of field access.
- if (internalIdField != null) {
- addNodeViewMethod(
- classDef,
- internalIdField,
- viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef).invoke(INTERNAL_ID),
- null
- );
- }
-
- // Creates view and change methods for parent schema.
- for (Field schemaField : schemaFields) {
- FieldDefinition schemaFieldDef = fieldDefs.get(fieldName(schemaField));
-
- addNodeViewMethod(
- classDef,
- schemaField,
- viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, schemaFieldDef),
- null
- );
-
- // Read only.
- if (isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
- continue;
- }
-
- MethodDefinition changeMtd0 = addNodeChangeMethod(
- classDef,
- schemaField,
- changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, schemaFieldDef),
- (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, parentInnerNodeFieldDef, schemaFieldDef),
- null
- );
-
- addNodeChangeBridgeMethod(classDef, schemaClassInfo.changeClassName, changeMtd0);
- }
-
- FieldDefinition polymorphicTypeIdFieldDef = fieldDefs.get(polymorphicIdField(schemaClass).getName());
-
- // Creates view and change methods for specific polymorphic instance schema.
- for (Field polymorphicField : polymorphicFields) {
- FieldDefinition polymorphicFieldDef = fieldDefs.get(fieldName(polymorphicField));
-
- addNodeViewMethod(
- classDef,
- polymorphicField,
- viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, polymorphicFieldDef),
- viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, polymorphicTypeIdFieldDef)
- );
-
- MethodDefinition changeMtd0 = addNodeChangeMethod(
- classDef,
- polymorphicField,
- changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, polymorphicFieldDef),
- (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, parentInnerNodeFieldDef, polymorphicFieldDef),
- changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, polymorphicTypeIdFieldDef)
- );
-
- addNodeChangeBridgeMethod(classDef, polymorphicExtensionClassInfo.changeClassName, changeMtd0);
- }
-
- ParameterizedType returnType = typeFromJavaClassName(schemaClassInfo.changeClassName);
-
- // Creates Node#convert(Class<T> changeClass).
- MethodDefinition convertByChangeClassMtd = classDef.declareMethod(
- of(PUBLIC),
- CONVERT_MTD_NAME,
- returnType,
- arg("changeClass", Class.class)
- );
-
- // return this.this$0.convert(changeClass);
- convertByChangeClassMtd.getBody()
- .append(getThisFieldCode(convertByChangeClassMtd, parentInnerNodeFieldDef))
- .append(convertByChangeClassMtd.getScope().getVariable("changeClass"))
- .invokeVirtual(schemaInnerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(Class.class))
- .retObject();
-
- // Creates Node#convert(String polymorphicId).
- MethodDefinition convertByPolymorphicTypeIdMtd = classDef.declareMethod(
- of(PUBLIC),
- CONVERT_MTD_NAME,
- returnType,
- arg("polymorphicTypeId", String.class)
- );
-
- // return this.this$0.convert(polymorphicTypeId);
- convertByPolymorphicTypeIdMtd.getBody()
- .append(getThisFieldCode(convertByPolymorphicTypeIdMtd, parentInnerNodeFieldDef))
- .append(convertByPolymorphicTypeIdMtd.getScope().getVariable("polymorphicTypeId"))
- .invokeVirtual(schemaInnerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(String.class))
- .retObject();
-
- // Creates ConstructableTreeNode#construct.
- MethodDefinition constructMtd = classDef.declareMethod(
- of(PUBLIC),
- CONSTRUCT_MTD_NAME,
- type(void.class),
- arg("key", type(String.class)),
- arg("src", type(ConfigurationSource.class)),
- arg("includeInternal", type(boolean.class))
- ).addException(NoSuchElementException.class);
-
- // return this.this$0.construct(key, src, includeInternal);
- constructMtd.getBody()
- .append(getThisFieldCode(constructMtd, parentInnerNodeFieldDef))
- .append(constructMtd.getScope().getVariable("key"))
- .append(constructMtd.getScope().getVariable("src"))
- .append(constructMtd.getScope().getVariable("includeInternal"))
- .invokeVirtual(
- schemaInnerNodeClassDef.getType(),
- CONSTRUCT_MTD_NAME,
- type(void.class),
- type(String.class), type(ConfigurationSource.class), type(boolean.class)
- )
- .ret();
-
- // Creates ConstructableTreeNode#copy.
- MethodDefinition copyMtd = classDef.declareMethod(
- of(PUBLIC),
- "copy",
- type(ConstructableTreeNode.class)
- );
-
- // return this.this$0.copy();
- copyMtd.getBody()
- .append(getThisFieldCode(copyMtd, parentInnerNodeFieldDef))
- .invokeVirtual(schemaInnerNodeClassDef.getType(), "copy", type(ConstructableTreeNode.class))
- .retObject();
-
- return classDef;
- }
-
- /**
- * Create a {@code *CfgImpl} for the polymorphic configuration instance schema.
- *
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtension Polymorphic configuration instance schema (child).
- * @param schemaCfgImplClassDef {@link DynamicConfiguration} definition for the polymorphic configuration schema {@code schemaClass}.
- * @param schemaFields Schema fields of polymorphic configuration {@code schemaClass}.
- * @param polymorphicFields Schema fields of a polymorphic configuration instance {@code polymorphicExtension}.
- * @param internalIdField Internal id field or {@code null} if it's not present.
- */
- private ClassDefinition createPolymorphicExtensionCfgImplClass(
- Class<?> schemaClass,
- Class<?> polymorphicExtension,
- ClassDefinition schemaCfgImplClassDef,
- Collection<Field> schemaFields,
- Collection<Field> polymorphicFields,
- @Nullable Field internalIdField
- ) {
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
- SchemaClassesInfo polymorphicExtensionClassInfo = schemasInfo.get(polymorphicExtension);
-
- // Configuration impl class definition.
- ClassDefinition classDef = new ClassDefinition(
- of(PUBLIC, FINAL),
- internalName(polymorphicExtensionClassInfo.cfgImplClassName),
- type(ConfigurationTreeWrapper.class),
- configClassInterfaces(polymorphicExtension, Set.of())
- );
-
- // private final ParentCfgImpl this$0;
- FieldDefinition parentCfgImplFieldDef = classDef.declareField(
- of(PRIVATE, FINAL),
- "this$0",
- typeFromJavaClassName(schemaClassInfo.cfgImplClassName)
- );
-
- // Constructor.
- MethodDefinition constructorMtd = classDef.declareConstructor(
- of(PUBLIC),
- arg("delegate", typeFromJavaClassName(schemaClassInfo.cfgImplClassName))
- );
-
- Variable delegateVar = constructorMtd.getScope().getVariable("delegate");
-
- // Constructor body.
- // super(parent);
- // this.this$0 = parent;
- constructorMtd.getBody()
- .append(constructorMtd.getThis())
- .append(delegateVar)
- .invokeConstructor(ConfigurationTreeWrapper.class, ConfigurationTree.class)
- .append(constructorMtd.getThis().setField(
- parentCfgImplFieldDef,
- delegateVar
- ))
- .ret();
-
- Map<String, FieldDefinition> fieldDefs = schemaCfgImplClassDef.getFields().stream()
- .collect(toMap(FieldDefinition::getName, identity()));
-
- if (internalIdField != null) {
- addConfigurationImplGetMethod(classDef, internalIdField, parentCfgImplFieldDef, fieldDefs.get(internalIdField.getName()));
- }
-
- for (Field schemaField : concat(schemaFields, polymorphicFields)) {
- addConfigurationImplGetMethod(classDef, schemaField, parentCfgImplFieldDef, fieldDefs.get(fieldName(schemaField)));
- }
-
- return classDef;
- }
-
- /**
- * Adds a {@link InnerNode#specificNode} override for the polymorphic configuration case.
- *
- * @param classDef Definition of a polymorphic configuration class (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private void addNodeSpecificNodeMethod(
- ClassDefinition classDef,
- Set<Class<?>> polymorphicExtensions,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition specificNodeMtd = classDef.declareMethod(
- of(PUBLIC),
- SPECIFIC_NODE_MTD.getName(),
- type(Object.class)
- );
-
- StringSwitchBuilder switchBuilder = typeIdSwitchBuilder(specificNodeMtd, polymorphicTypeIdFieldDef);
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- SchemaClassesInfo polymorphicExtensionClassInfo = schemasInfo.get(polymorphicExtension);
-
- switchBuilder.addCase(
- polymorphicInstanceId(polymorphicExtension),
- newInstance(
- typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
- specificNodeMtd.getThis()
- ).ret()
- );
- }
-
- specificNodeMtd.getBody().append(switchBuilder.build());
- }
-
- /**
- * Adds a {@code *Node#convert(Class changeClass)} and {@code *Node#convert(String polymorphicTypeId)} for the polymorphic configuration
- * case.
- *
- * @param classDef Definition of a polymorphic configuration class {@code schemaClass}.
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param changePolymorphicTypeIdMtd Method for changing the type of polymorphic configuration.
- */
- private void addNodeConvertMethods(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- MethodDefinition changePolymorphicTypeIdMtd
- ) {
- SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
-
- MethodDefinition convertByChangeClassMtd = classDef.declareMethod(
- of(PUBLIC),
- CONVERT_MTD_NAME,
- typeFromJavaClassName(schemaClassInfo.changeClassName),
- arg("changeClass", Class.class)
- );
-
- MethodDefinition convertByPolymorphicTypeIdMtd = classDef.declareMethod(
- of(PUBLIC),
- CONVERT_MTD_NAME,
- typeFromJavaClassName(schemaClassInfo.changeClassName),
- arg("polymorphicTypeId", String.class)
- );
-
- // changeClass.getName();
- BytecodeExpression changeClassName = convertByChangeClassMtd.getScope()
- .getVariable("changeClass")
- .invoke(CLASS_GET_NAME_MTD);
-
- StringSwitchBuilder switchByChangeClassBuilder = new StringSwitchBuilder(convertByChangeClassMtd.getScope())
- .expression(changeClassName)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, changeClassName));
-
- Variable polymorphicTypeId = convertByPolymorphicTypeIdMtd.getScope()
- .getVariable("polymorphicTypeId");
-
- StringSwitchBuilder switchByPolymorphicTypeIdBuilder = new StringSwitchBuilder(convertByPolymorphicTypeIdMtd.getScope())
- .expression(polymorphicTypeId)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, polymorphicTypeId));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- SchemaClassesInfo polymorphicExtensionClassInfo = schemasInfo.get(polymorphicExtension);
-
- String polymorphicInstanceId = polymorphicInstanceId(polymorphicExtension);
-
- // case "HashIndexChange":
- // this.changePolymorphicTypeId("hashIndex");
- // return new HashIndexNode(this);
- switchByChangeClassBuilder.addCase(
- polymorphicExtensionClassInfo.changeClassName,
- new BytecodeBlock()
- .append(constantString(polymorphicInstanceId))
- .invokeVirtual(changePolymorphicTypeIdMtd)
- .append(newInstance(
- typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
- convertByChangeClassMtd.getThis()
- ))
- .retObject()
- );
-
- // case "hashIndex":
- // this.changePolymorphicTypeId("hashIndex");
- // return new HashIndexNode(this);
- switchByPolymorphicTypeIdBuilder.addCase(
- polymorphicInstanceId,
- new BytecodeBlock()
- .append(constantString(polymorphicInstanceId))
- .invokeVirtual(changePolymorphicTypeIdMtd)
- .append(newInstance(
- typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
- convertByPolymorphicTypeIdMtd.getThis()
- ))
- .retObject()
- );
- }
-
- convertByChangeClassMtd.getBody()
- .append(convertByChangeClassMtd.getThis())
- .append(switchByChangeClassBuilder.build())
- .ret();
-
- convertByPolymorphicTypeIdMtd.getBody()
- .append(convertByPolymorphicTypeIdMtd.getThis())
- .append(switchByPolymorphicTypeIdBuilder.build())
- .ret();
- }
-
- /**
- * Adds a {@code Node#changeTypeId} for the polymorphic configuration case.
- *
- * @param classDef Definition of a polymorphic configuration class (parent).
- * @param fieldDefs Definitions for all fields in {@code classDef}.
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param polymorphicFields Fields of polymorphic extensions.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- * @return Method definition.
- */
- private MethodDefinition addNodeChangePolymorphicTypeIdMethod(
- ClassDefinition classDef,
- Map<String, FieldDefinition> fieldDefs,
- Set<Class<?>> polymorphicExtensions,
- Collection<Field> polymorphicFields,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition changePolymorphicTypeIdMtd = classDef.declareMethod(
- of(PUBLIC),
- changeMethodName(polymorphicTypeIdFieldDef.getName()),
- type(void.class),
- arg("typeId", String.class)
- );
-
- Variable typeIdVar = changePolymorphicTypeIdMtd.getScope().getVariable("typeId");
-
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(changePolymorphicTypeIdMtd.getScope())
- .expression(typeIdVar)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, typeIdVar));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- // Fields that need to be cleared when changing the type of the polymorphic configuration instance.
- Collection<Field> resetFields = polymorphicFields.stream()
- .filter(f -> !polymorphicExtension.equals(f.getDeclaringClass()))
- .collect(toList());
-
- // this.typeId = typeId;
- BytecodeBlock codeBlock = new BytecodeBlock()
- .append(setThisFieldCode(changePolymorphicTypeIdMtd, typeIdVar, polymorphicTypeIdFieldDef));
-
- // Reset fields.
- for (Field resetField : resetFields) {
- FieldDefinition fieldDef = fieldDefs.get(fieldName(resetField));
-
- if (isValue(resetField) || isConfigValue(resetField)) {
- // this.field = null;
- codeBlock.append(setThisFieldCode(
- changePolymorphicTypeIdMtd,
- constantNull(fieldDef.getType()),
- fieldDef
- ));
- } else {
- // this.field = new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName");
- codeBlock.append(setThisFieldCode(changePolymorphicTypeIdMtd, newNamedListNode(resetField), fieldDef));
- }
- }
-
- // ConfigurationUtil.addDefaults(this);
- codeBlock
- .append(changePolymorphicTypeIdMtd.getThis())
- .invokeStatic(ADD_DEFAULTS_MTD);
-
- switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), codeBlock);
- }
-
- // if(typeId.equals(this.typeId)) return;
- // else switch(typeId)...
- changePolymorphicTypeIdMtd.getBody()
- .append(typeIdVar)
- .append(getThisFieldCode(changePolymorphicTypeIdMtd, polymorphicTypeIdFieldDef))
- .append(
- new IfStatement()
- .condition(new BytecodeBlock().invokeVirtual(STRING_EQUALS_MTD))
- .ifTrue(new BytecodeBlock().ret())
- .ifFalse(switchBuilder.build().ret())
- );
-
- return changePolymorphicTypeIdMtd;
- }
-
- /**
- * Adds a {@link DynamicConfiguration#specificConfigTree} override for the polymorphic configuration case.
- *
- * @param classDef Definition of a polymorphic configuration class (parent).
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private void addCfgSpecificConfigTreeMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition specificConfigMtd = classDef.declareMethod(
- of(PUBLIC),
- SPECIFIC_CONFIG_TREE_MTD.getName(),
- type(ConfigurationTree.class)
- );
-
- // String tmpStr;
- Variable tmpStrVar = specificConfigMtd.getScope().createTempVariable(String.class);
-
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(specificConfigMtd.getScope())
- .expression(tmpStrVar)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- // return new SpecialCfgImpl(this);
- switchBuilder.addCase(
- polymorphicInstanceId(polymorphicExtension),
- newInstance(
- typeFromJavaClassName(schemasInfo.get(polymorphicExtension).cfgImplClassName),
- specificConfigMtd.getThis()
- ).ret()
- );
- }
-
- // ConfigNode
- ParameterizedType nodeType = typeFromJavaClassName(schemasInfo.get(schemaClass).nodeClassName);
-
- // Object tmpObj;
- Variable tmpObjVar = specificConfigMtd.getScope().createTempVariable(Object.class);
-
- // tmpObj = this.refreshValue();
- // tmpStr = ((ConfigNode) tmpObj).typeId;
- // switch(tmpStr) ...
- specificConfigMtd.getBody()
- .append(tmpObjVar.set(specificConfigMtd.getThis().invoke(REFRESH_VALUE_MTD)))
- .append(tmpStrVar.set(tmpObjVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
- .append(switchBuilder.build())
- .ret();
- }
-
- /**
- * Adds a {@code DynamicConfiguration#removeMembers} override for the polymorphic configuration case.
- *
- * @param classDef Definition of a polymorphic configuration class (parent).
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param fieldDefs Field definitions for all fields of {@code classDef}.
- * @param polymorphicFields Fields of polymorphic extensions.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private void addCfgRemoveMembersMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> polymorphicFields,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition removeMembersMtd = classDef.declareMethod(
- of(PUBLIC),
- "removeMembers",
- type(void.class),
- arg("oldValue", type(Object.class)),
- arg("members", type(Map.class))
- );
-
- // InnerNode oldValue;
- Variable oldValueVar = removeMembersMtd.getScope().getVariable("oldValue");
-
- // Map members;
- Variable membersVar = removeMembersMtd.getScope().getVariable("members");
-
- // String tmpStr;
- Variable tmpStrVar = removeMembersMtd.getScope().createTempVariable(String.class);
-
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(removeMembersMtd.getScope())
- .expression(tmpStrVar)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- Collection<Field> removeFields = polymorphicFields.stream()
- .filter(f -> !polymorphicExtension.equals(f.getDeclaringClass()))
- .collect(toList());
-
- BytecodeBlock blockCode = new BytecodeBlock();
-
- for (Field removeField : removeFields) {
- // this.removeMember(members, this.field);
- blockCode
- .append(removeMembersMtd.getThis())
- .append(membersVar)
- .append(getThisFieldCode(removeMembersMtd, fieldDefs.get(fieldName(removeField))))
- .invokeVirtual(REMOVE_MEMBER_MTD);
- }
-
- switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), blockCode);
- }
-
- // ConfigNode
- ParameterizedType nodeType = typeFromJavaClassName(schemasInfo.get(schemaClass).nodeClassName);
-
- // tmpStr = ((ConfigNode) oldValue).typeId;
- // switch(tmpStr) ...
- removeMembersMtd.getBody()
- .append(tmpStrVar.set(oldValueVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
- .append(switchBuilder.build())
- .ret();
- }
-
- /**
- * Adds a {@code DynamicConfiguration#addMembers} override for the polymorphic configuration case.
- *
- * @param classDef Definition of a polymorphic configuration class (parent).
- * @param schemaClass Polymorphic configuration schema (parent).
- * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
- * @param fieldDefs Field definitions for all fields of {@code classDef}.
- * @param polymorphicFields Fields of polymorphic extensions.
- * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
- */
- private void addCfgAddMembersMethod(
- ClassDefinition classDef,
- Class<?> schemaClass,
- Set<Class<?>> polymorphicExtensions,
- Map<String, FieldDefinition> fieldDefs,
- Collection<Field> polymorphicFields,
- FieldDefinition polymorphicTypeIdFieldDef
- ) {
- MethodDefinition removeMembersMtd = classDef.declareMethod(
- of(PUBLIC),
- "addMembers",
- type(void.class),
- arg("newValue", type(Object.class)),
- arg("members", type(Map.class))
- );
-
- // InnerNode newValue;
- Variable newValueVar = removeMembersMtd.getScope().getVariable("newValue");
-
- // Map members;
- Variable membersVar = removeMembersMtd.getScope().getVariable("members");
-
- // String tmpStr;
- Variable tmpStrVar = removeMembersMtd.getScope().createTempVariable(String.class);
-
- StringSwitchBuilder switchBuilder = new StringSwitchBuilder(removeMembersMtd.getScope())
- .expression(tmpStrVar)
- .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
-
- for (Class<?> polymorphicExtension : polymorphicExtensions) {
- Collection<Field> addFields = polymorphicFields.stream()
- .filter(f -> polymorphicExtension.equals(f.getDeclaringClass()))
- .collect(toList());
-
- BytecodeBlock blockCode = new BytecodeBlock();
-
- for (Field addField : addFields) {
- // this.addMember(members, this.field);
- blockCode
- .append(removeMembersMtd.getThis())
- .append(membersVar)
- .append(getThisFieldCode(removeMembersMtd, fieldDefs.get(fieldName(addField))))
- .invokeVirtual(ADD_MEMBER_MTD);
- }
-
- switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), blockCode);
- }
-
- // ConfigNode
- ParameterizedType nodeType = typeFromJavaClassName(schemasInfo.get(schemaClass).nodeClassName);
-
- // tmpStr = ((ConfigNode) newValue).typeId;
- // switch(tmpStr) ...
- removeMembersMtd.getBody()
- .append(tmpStrVar.set(newValueVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
- .append(switchBuilder.build())
- .ret();
- }
-
- /**
- * Creates bytecode block that invokes of construct methods for {@link InnerNode#construct(String, ConfigurationSource, boolean)}.
- *
- * @param constructMtd Method definition {@link InnerNode#construct(String, ConfigurationSource, boolean)} defined in {@code *Node}
- * class.
- * @param schemaField Schema field.
- * @param schemaFieldDef Schema field definition.
- * @return Bytecode block that invokes of construct method for field.
- */
- private BytecodeBlock treatSourceForConstruct(
- MethodDefinition constructMtd,
- Field schemaField,
- FieldDefinition schemaFieldDef
- ) {
- BytecodeBlock codeBlock = new BytecodeBlock();
-
- Variable thisVar = constructMtd.getThis();
- Variable srcVar = constructMtd.getScope().getVariable("src");
-
- if (isValue(schemaField)) {
- // this.field = src == null ? null : src.unwrap(FieldType.class);
- codeBlock.append(thisVar.setField(schemaFieldDef, inlineIf(
- isNull(srcVar),
- constantNull(schemaFieldDef.getType()),
- srcVar.invoke(UNWRAP, constantClass(schemaFieldDef.getType())).cast(schemaFieldDef.getType())
- )));
- } else if (isConfigValue(schemaField)) {
- BytecodeNode setField;
-
- ParameterizedType fieldDefType = schemaFieldDef.getType();
-
- if (isPolymorphicConfig(schemaField.getType())) {
- Field polymorphicIdField = polymorphicIdField(schemaField.getType());
-
- assert polymorphicIdField != null : schemaField.getType().getName();
-
- // this.field;
- BytecodeExpression thisField = getThisFieldCode(constructMtd, schemaFieldDef);
-
- // String tmpStr;
- Variable tmpStrVar = constructMtd.getScope().createTempVariable(String.class);
-
- // this.field = (FieldType) this.field.copy();
- // if(tmpStr != null) this.field.changeTypeId(tmpStr);
- BytecodeBlock copyWithChange = new BytecodeBlock()
- .append(setThisFieldCode(constructMtd, thisField.invoke(COPY).cast(fieldDefType), schemaFieldDef))
- .append(new IfStatement()
- .condition(isNotNull(tmpStrVar))
- .ifTrue(thisField.invoke(changeMethodName(polymorphicIdField.getName()), void.class, tmpStrVar))
- );
-
- // this.field = new FieldType();
- // if(tmpStr != null) this.field.changeTypeId(tmpStr);
- // else {
- // this.field.constructDefault("typeId");
- // if(this.field.typeId == null) throw new IllegalStateException();
- // }
- BytecodeBlock newInstanceWithChange = new BytecodeBlock()
- .append(setThisFieldCode(constructMtd, newInstance(fieldDefType), schemaFieldDef))
- .append(new IfStatement()
- .condition(isNotNull(tmpStrVar))
- .ifTrue(thisField.invoke(changeMethodName(polymorphicIdField.getName()), void.class, tmpStrVar))
- .ifFalse(new BytecodeBlock()
- .append(thisField.invoke(CONSTRUCT_DEFAULT_MTD, constantString(polymorphicIdField.getName())))
- .append(new IfStatement()
- .condition(isNull(thisField.getField(polymorphicIdField.getName(), String.class)))
- .ifTrue(throwException(
- IllegalStateException.class,
- constantString(polymorphicTypeNotDefinedErrorMessage(
- polymorphicIdField))
- ))
- )
- )
- );
-
- // tmpStr = src.polymorphicTypeId("typeId");
- // if(this.field == null)
- setField = new BytecodeBlock()
- .append(tmpStrVar.set(srcVar.invoke(POLYMORPHIC_TYPE_ID_MTD, constantString(polymorphicIdField.getName()))))
- .append(new IfStatement()
- .condition(isNull(thisField))
- .ifTrue(newInstanceWithChange)
- .ifFalse(copyWithChange)
- );
- } else {
- // newValue = this.field == null ? new ValueNode() : field.copy());
- BytecodeExpression newValue = newOrCopyNodeField(schemaField, getThisFieldCode(constructMtd, schemaFieldDef));
-
- // this.field = newValue;
- setField = setThisFieldCode(constructMtd, newValue, schemaFieldDef);
- }
-
- if (containsNameAnnotation(schemaField)) {
- setField = new BytecodeBlock()
- .append(setField)
- .append(getThisFieldCode(constructMtd, schemaFieldDef).invoke(
- SET_INJECTED_NAME_FIELD_VALUE_MTD,
- constantString(schemaField.getAnnotation(Name.class).value())
- ));
- }
-
- codeBlock.append(
- new IfStatement()
- .condition(isNull(srcVar))
- .ifTrue(setThisFieldCode(constructMtd, constantNull(fieldDefType), schemaFieldDef))
- .ifFalse(new BytecodeBlock()
- .append(setField)
- .append(srcVar.invoke(DESCEND, thisVar.getField(schemaFieldDef)))
- )
- );
- } else {
- // this.field = src == null ? new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName")
- // : src.descend(field = field.copy()));
- codeBlock.append(new IfStatement()
- .condition(isNull(srcVar))
- .ifTrue(setThisFieldCode(constructMtd, newNamedListNode(schemaField), schemaFieldDef))
- .ifFalse(new BytecodeBlock()
- .append(setThisFieldCode(
- constructMtd,
- thisVar.getField(schemaFieldDef).invoke(COPY).cast(schemaFieldDef.getType()),
- schemaFieldDef
- )).append(srcVar.invoke(DESCEND, thisVar.getField(schemaFieldDef)))
- )
- );
- }
-
- return codeBlock;
- }
-
- private String polymorphicTypeNotDefinedErrorMessage(Field polymorphicIdField) {
- return "Polymorphic configuration type is not defined: "
- + polymorphicIdField.getDeclaringClass().getName()
- + ". See @" + PolymorphicConfig.class.getSimpleName() + " documentation.";
- }
-
- /**
- * Creates a bytecode block of code that sets the default value for a field from the schema for {@link
- * InnerNode#constructDefault(String)}.
- *
- * @param constructDfltMtd Method definition {@link InnerNode#constructDefault(String)} defined in {@code *Node} class.
- * @param schemaField Schema field.
- * @param schemaFieldDef Schema field definition.
- * @param specFieldDef Definition of the schema field.: {@code _spec#}.
- * @return Bytecode block that sets the default value for a field from the schema.
- */
- private static BytecodeBlock addNodeConstructDefault(
- MethodDefinition constructDfltMtd,
- Field schemaField,
- FieldDefinition schemaFieldDef,
- FieldDefinition specFieldDef
- ) {
- Variable thisVar = constructDfltMtd.getThis();
-
- // defaultValue = _spec#.field;
- BytecodeExpression defaultValue = thisVar.getField(specFieldDef).getField(schemaField);
-
- Class<?> schemaFieldType = schemaField.getType();
-
- // defaultValue = Box.valueOf(defaultValue); // Boxing.
- if (schemaFieldType.isPrimitive()) {
- defaultValue = invokeStatic(
- schemaFieldDef.getType(),
- "valueOf",
- schemaFieldDef.getType(),
- singleton(defaultValue)
- );
- }
-
- // defaultValue = defaultValue.clone();
- if (schemaFieldType.isArray()) {
- defaultValue = defaultValue.invoke("clone", Object.class).cast(schemaFieldType);
- }
-
- // this.field = defaultValue;
- return new BytecodeBlock().append(thisVar.setField(schemaFieldDef, defaultValue));
- }
-
- /**
- * Creates the bytecode {@code throw new Exception(msg);}.
+ * Creates the bytecode {@code throw new Exception(msg);}.
*
* @param throwableClass Exception class.
* @param parameters Exception constructor parameters.
* @return Exception throwing bytecode.
*/
- private static BytecodeBlock throwException(
+ static BytecodeBlock throwException(
Class<? extends Throwable> throwableClass,
BytecodeExpression... parameters
) {
@@ -3224,7 +456,7 @@ public class ConfigurationAsmGenerator {
* @param f Configuration schema field.
* @return Field name.
*/
- private static String fieldName(Field f) {
+ static String fieldName(Field f) {
return isPolymorphicConfigInstance(f.getDeclaringClass())
? f.getName() + "#" + polymorphicInstanceId(f.getDeclaringClass()) : f.getName();
}
@@ -3236,7 +468,7 @@ public class ConfigurationAsmGenerator {
* @param typeIdFieldDef Field definition that contains the id of the polymorphic configuration instance.
* @return String switch builder by {@code typeIdFieldDef}.
*/
- private static StringSwitchBuilder typeIdSwitchBuilder(MethodDefinition mtdDef, FieldDefinition typeIdFieldDef) {
+ static StringSwitchBuilder typeIdSwitchBuilder(MethodDefinition mtdDef, FieldDefinition typeIdFieldDef) {
BytecodeExpression typeIdVar = mtdDef.getThis().getField(typeIdFieldDef);
return new StringSwitchBuilder(mtdDef.getScope())
@@ -3251,7 +483,7 @@ public class ConfigurationAsmGenerator {
* @param fieldDefs Field definitions.
* @return Bytecode for getting the field.
*/
- private static BytecodeExpression getThisFieldCode(MethodDefinition mtdDef, FieldDefinition... fieldDefs) {
+ static BytecodeExpression getThisFieldCode(MethodDefinition mtdDef, FieldDefinition... fieldDefs) {
assert !nullOrEmpty(fieldDefs);
// this.field;
@@ -3273,7 +505,7 @@ public class ConfigurationAsmGenerator {
* @param fieldDefs Field definitions.
* @return Bytecode for setting the field value.
*/
- private static BytecodeExpression setThisFieldCode(
+ static BytecodeExpression setThisFieldCode(
MethodDefinition mtdDef,
BytecodeExpression value,
FieldDefinition... fieldDefs
@@ -3304,7 +536,7 @@ public class ConfigurationAsmGenerator {
* @return Configuration schema field with {@link PolymorphicId}.
*/
@Nullable
- private static Field polymorphicIdField(Class<?> schemaClass) {
+ static Field polymorphicIdField(Class<?> schemaClass) {
assert isPolymorphicConfig(schemaClass) : schemaClass.getName();
for (Field f : schemaClass.getDeclaredFields()) {
@@ -3322,7 +554,7 @@ public class ConfigurationAsmGenerator {
* @param schemaField Configuration schema field.
* @return Name of the method to change the field.
*/
- private static String changeMethodName(String schemaField) {
+ static String changeMethodName(String schemaField) {
return "change" + capitalize(schemaField);
}
@@ -3332,7 +564,7 @@ public class ConfigurationAsmGenerator {
* @param schemaField Schema field with {@link NamedConfigValue}.
* @return Bytecode like: new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName");
*/
- private BytecodeExpression newNamedListNode(Field schemaField) {
+ BytecodeExpression newNamedListNode(Field schemaField) {
assert isNamedConfigValue(schemaField) : schemaField;
Class<?> fieldType = schemaField.getType();
@@ -3352,94 +584,4 @@ public class ConfigurationAsmGenerator {
isPolymorphicConfig(fieldType) ? constantString(polymorphicIdField(fieldType).getName()) : constantNull(String.class)
);
}
-
- /**
- * Adds method overrides {@link InnerNode#getInjectedNameFieldValue} and {@link InnerNode#setInjectedNameFieldValue}.
- *
- * @param classDef Node class definition.
- * @param injectedNameFieldDef Field definition with {@link InjectedName}.
- */
- private void addInjectedNameFieldMethods(ClassDefinition classDef, FieldDefinition injectedNameFieldDef) {
- MethodDefinition getInjectedNameFieldValueMtd = classDef.declareMethod(
- of(PUBLIC),
- "getInjectedNameFieldValue",
- type(String.class)
- );
-
- getInjectedNameFieldValueMtd.getBody()
- .append(getThisFieldCode(getInjectedNameFieldValueMtd, injectedNameFieldDef))
- .retObject();
-
- MethodDefinition setInjectedNameFieldValueMtd = classDef.declareMethod(
- of(PUBLIC),
- "setInjectedNameFieldValue",
- type(void.class),
- arg("value", String.class)
- );
-
- Variable valueVar = setInjectedNameFieldValueMtd.getScope().getVariable("value");
-
- setInjectedNameFieldValueMtd.getBody()
- .append(invokeStatic(REQUIRE_NON_NULL, valueVar, constantString("value")))
- .append(setThisFieldCode(
- setInjectedNameFieldValueMtd,
- valueVar,
- injectedNameFieldDef
- )).ret();
- }
-
- /**
- * Adds an override for the {@link InnerNode#isPolymorphic} method that returns {@code true}.
- *
- * @param innerNodeClassDef {@link InnerNode} class definition.
- */
- private static void addIsPolymorphicMethod(ClassDefinition innerNodeClassDef) {
- MethodDefinition mtd = innerNodeClassDef.declareMethod(
- of(PUBLIC),
- IS_POLYMORPHIC_MTD.getName(),
- type(boolean.class)
- );
-
- mtd.getBody()
- .push(true)
- .retBoolean();
- }
-
- /**
- * Adds an override for the {@link InnerNode#internalSchemaTypes} method that returns field {@code internalSchemaTypesFieldDef}.
- *
- * @param innerNodeClassDef {@link InnerNode} class definition.
- * @param internalSchemaTypesFieldDef Final field of {@link InnerNode}, which stores all schemes for internal configuration extensions.
- */
- private static void addInternalSchemaTypesMethod(
- ClassDefinition innerNodeClassDef,
- FieldDefinition internalSchemaTypesFieldDef
- ) {
- MethodDefinition mtd = innerNodeClassDef.declareMethod(
- of(PUBLIC),
- INTERNAL_SCHEMA_TYPES_MTD.getName(),
- type(Class[].class)
- );
-
- mtd.getBody()
- .append(getThisFieldCode(mtd, internalSchemaTypesFieldDef))
- .retObject();
- }
-
- /**
- * Adds an override for the {@link InnerNode#extendsAbstractConfiguration()} method that returns {@code true}.
- *
- * @param innerNodeClassDef {@link InnerNode} class definition.
- */
- private static void addIsExtendAbstractConfigurationMethod(ClassDefinition innerNodeClassDef) {
- MethodDefinition mtd = innerNodeClassDef.declareMethod(
- of(PUBLIC),
- "extendsAbstractConfiguration",
- type(boolean.class)
- );
-
- mtd.getBody()
- .push(true)
- .retBoolean();
- }
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationImplAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationImplAsmGenerator.java
new file mode 100644
index 0000000000..e646262d20
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationImplAsmGenerator.java
@@ -0,0 +1,932 @@
+/*
+ * 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.ignite.internal.configuration.asm;
+
+import static com.facebook.presto.bytecode.Access.FINAL;
+import static com.facebook.presto.bytecode.Access.PRIVATE;
+import static com.facebook.presto.bytecode.Access.PUBLIC;
+import static com.facebook.presto.bytecode.Access.STATIC;
+import static com.facebook.presto.bytecode.Access.SYNTHETIC;
+import static com.facebook.presto.bytecode.Parameter.arg;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromJavaClassName;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantBoolean;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantClass;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantString;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.inlineIf;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeDynamic;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newArray;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newInstance;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.set;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.fieldName;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.getThisFieldCode;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.internalName;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.setThisFieldCode;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.throwException;
+import static org.apache.ignite.internal.configuration.asm.DirectProxyAsmGenerator.newDirectProxyLambda;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.configurationClassName;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.nodeClassName;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInjectedName;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInternalId;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isNamedConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfig;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfigInstance;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicId;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.polymorphicInstanceId;
+import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
+import static org.apache.ignite.internal.util.CollectionUtils.concat;
+import static org.objectweb.asm.Type.getMethodType;
+import static org.objectweb.asm.Type.getType;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.ClassDefinition;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodDefinition;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.Variable;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import org.apache.ignite.configuration.ConfigurationProperty;
+import org.apache.ignite.configuration.ConfigurationTree;
+import org.apache.ignite.configuration.ConfigurationValue;
+import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdException;
+import org.apache.ignite.configuration.NamedConfigurationTree;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.internal.configuration.ConfigurationNode;
+import org.apache.ignite.internal.configuration.ConfigurationTreeWrapper;
+import org.apache.ignite.internal.configuration.DynamicConfiguration;
+import org.apache.ignite.internal.configuration.DynamicConfigurationChanger;
+import org.apache.ignite.internal.configuration.DynamicProperty;
+import org.apache.ignite.internal.configuration.NamedListConfiguration;
+import org.apache.ignite.internal.configuration.direct.DirectPropertyProxy;
+import org.apache.ignite.internal.configuration.tree.InnerNode;
+import org.jetbrains.annotations.Nullable;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+
+class ConfigurationImplAsmGenerator extends AbstractAsmGenerator {
+ /** {@link DynamicConfiguration#DynamicConfiguration} constructor. */
+ private static final Constructor<?> DYNAMIC_CONFIGURATION_CTOR;
+
+ /** {@code DynamicConfiguration#add} method. */
+ private static final Method DYNAMIC_CONFIGURATION_ADD_MTD;
+
+ /** {@code ConfigurationNode#refreshValue} method. */
+ private static final Method REFRESH_VALUE_MTD;
+
+ /** {@code DynamicConfiguration#addMember} method. */
+ private static final Method ADD_MEMBER_MTD;
+
+ /** {@code DynamicConfiguration#removeMember} method. */
+ private static final Method REMOVE_MEMBER_MTD;
+
+ /** {@link DynamicConfiguration#specificConfigTree} method. */
+ private static final Method SPECIFIC_CONFIG_TREE_MTD;
+
+ /** {@code ConfigurationNode#currentValue}. */
+ private static final Method CURRENT_VALUE_MTD;
+
+ /** {@link DynamicConfiguration#isRemovedFromNamedList}. */
+ private static final Method IS_REMOVED_FROM_NAMED_LIST_MTD;
+
+ /** Field name for method {@link DynamicConfiguration#internalConfigTypes}. */
+ private static final String INTERNAL_CONFIG_TYPES_FIELD_NAME = "_internalConfigTypes";
+
+ static {
+ try {
+ DYNAMIC_CONFIGURATION_CTOR = DynamicConfiguration.class.getDeclaredConstructor(
+ List.class,
+ String.class,
+ RootKey.class,
+ DynamicConfigurationChanger.class,
+ boolean.class
+ );
+
+ DYNAMIC_CONFIGURATION_ADD_MTD = DynamicConfiguration.class.getDeclaredMethod(
+ "add",
+ ConfigurationProperty.class
+ );
+
+ REFRESH_VALUE_MTD = ConfigurationNode.class.getDeclaredMethod("refreshValue");
+
+ ADD_MEMBER_MTD = DynamicConfiguration.class.getDeclaredMethod("addMember", Map.class, ConfigurationProperty.class);
+
+ REMOVE_MEMBER_MTD = DynamicConfiguration.class.getDeclaredMethod("removeMember", Map.class, ConfigurationProperty.class);
+
+ SPECIFIC_CONFIG_TREE_MTD = DynamicConfiguration.class.getDeclaredMethod("specificConfigTree");
+
+ CURRENT_VALUE_MTD = ConfigurationNode.class.getDeclaredMethod("currentValue");
+
+ IS_REMOVED_FROM_NAMED_LIST_MTD = DynamicConfiguration.class.getDeclaredMethod("isRemovedFromNamedList");
+ } catch (NoSuchMethodException nsme) {
+ throw new ExceptionInInitializerError(nsme);
+ }
+ }
+
+ /** Class definition that extends the {@link DynamicConfiguration}. */
+ private ClassDefinition cfgImplClassDef;
+
+ ConfigurationImplAsmGenerator(
+ ConfigurationAsmGenerator cgen,
+ Class<?> schemaClass,
+ Set<Class<?>> internalExtensions,
+ Set<Class<?>> polymorphicExtensions,
+ List<Field> schemaFields,
+ Collection<Field> internalFields,
+ Collection<Field> polymorphicFields,
+ @Nullable Field internalIdField
+ ) {
+ super(
+ cgen,
+ schemaClass,
+ internalExtensions,
+ polymorphicExtensions,
+ schemaFields,
+ internalFields,
+ polymorphicFields,
+ internalIdField
+ );
+ }
+
+ @Override
+ public List<ClassDefinition> generate() {
+ assert cfgImplClassDef == null;
+
+ List<ClassDefinition> classDefs = new ArrayList<>();
+
+ classDefs.add(createCfgImplClass());
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ // Only the fields of a specific instance of a polymorphic configuration.
+ Collection<Field> polymorphicFields = this.polymorphicFields.stream()
+ .filter(f -> f.getDeclaringClass() == polymorphicExtension)
+ .collect(toList());
+
+ classDefs.add(createPolymorphicExtensionCfgImplClass(polymorphicExtension, polymorphicFields));
+ }
+
+ return classDefs;
+ }
+
+ /**
+ * Construct a {@link DynamicConfiguration} definition for a configuration schema.
+ *
+ * @return Constructed {@link DynamicConfiguration} definition for the configuration schema.
+ */
+ private ClassDefinition createCfgImplClass() {
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+
+ // Configuration impl class definition.
+ cfgImplClassDef = new ClassDefinition(
+ EnumSet.of(PUBLIC, FINAL),
+ internalName(schemaClassInfo.cfgImplClassName),
+ type(DynamicConfiguration.class),
+ cgen.configClassInterfaces(schemaClass, internalExtensions)
+ );
+
+ Map<String, FieldDefinition> fieldDefs = new HashMap<>();
+
+ // To store the id of the polymorphic configuration instance.
+ FieldDefinition polymorphicTypeIdFieldDef = null;
+
+ for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
+ String fieldName = fieldName(schemaField);
+
+ FieldDefinition fieldDef = addConfigurationImplField(schemaField, fieldName);
+
+ fieldDefs.put(fieldName, fieldDef);
+
+ if (isPolymorphicId(schemaField)) {
+ polymorphicTypeIdFieldDef = fieldDef;
+ }
+ }
+
+ if (internalIdField != null) {
+ // Internal id dynamic property is stored as a regular field.
+ String fieldName = internalIdField.getName();
+
+ FieldDefinition fieldDef = addConfigurationImplField(internalIdField, fieldName);
+
+ fieldDefs.put(fieldName, fieldDef);
+ }
+
+ FieldDefinition internalConfigTypesFieldDef = null;
+
+ if (!internalExtensions.isEmpty()) {
+ internalConfigTypesFieldDef = cfgImplClassDef.declareField(
+ EnumSet.of(PRIVATE, FINAL),
+ INTERNAL_CONFIG_TYPES_FIELD_NAME,
+ Class[].class
+ );
+ }
+
+ // Constructor
+ addConfigurationImplConstructor(fieldDefs, internalConfigTypesFieldDef);
+
+ // org.apache.ignite.internal.configuration.DynamicProperty#directProxy
+ addDirectProxyMethod(schemaClassInfo);
+
+ // Getter for the internal id.
+ if (internalIdField != null) {
+ addConfigurationImplGetMethod(cfgImplClassDef, internalIdField, fieldDefs.get(internalIdField.getName()));
+ }
+
+ for (Field schemaField : concat(schemaFields, internalFields)) {
+ addConfigurationImplGetMethod(cfgImplClassDef, schemaField, fieldDefs.get(fieldName(schemaField)));
+ }
+
+ // org.apache.ignite.internal.configuration.DynamicConfiguration#configType
+ addCfgImplConfigTypeMethod(typeFromJavaClassName(schemaClassInfo.cfgClassName));
+
+ if (internalConfigTypesFieldDef != null) {
+ addCfgImplInternalConfigTypesMethod(cfgImplClassDef, internalConfigTypesFieldDef);
+ }
+
+ if (!polymorphicExtensions.isEmpty()) {
+ addCfgSpecificConfigTreeMethod(polymorphicTypeIdFieldDef);
+
+ addCfgRemoveMembersMethod(fieldDefs, polymorphicTypeIdFieldDef);
+
+ addCfgAddMembersMethod(fieldDefs, polymorphicTypeIdFieldDef);
+
+ addCfgImplPolymorphicInstanceConfigTypeMethod(polymorphicTypeIdFieldDef);
+ }
+
+ return cfgImplClassDef;
+ }
+
+ /**
+ * Declares a field that corresponds to configuration value. Depending on the schema, the following options are possible:
+ * <ul>
+ * <li>
+ * {@code @Value public type fieldName}<br/>becomes<br/>
+ * {@code public DynamicProperty fieldName}
+ * </li>
+ * <li>
+ * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
+ * {@code public MyConfiguration fieldName}
+ * </li>
+ * <li>
+ * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
+ * {@code public NamedListConfiguration fieldName}
+ * </li>
+ * <li>
+ * {@code @PolymorphicId public String fieldName}<br/>becomes<br/>
+ * {@code public String fieldName}
+ * </li>
+ * </ul>
+ *
+ * @param schemaField Configuration Schema class field.
+ * @param fieldName Field name, if {@code null} will be used {@link Field#getName}.
+ * @return Declared field definition.
+ */
+ private FieldDefinition addConfigurationImplField(
+ Field schemaField,
+ String fieldName
+ ) {
+ ParameterizedType fieldType;
+
+ if (isConfigValue(schemaField)) {
+ fieldType = typeFromJavaClassName(cgen.schemaInfo(schemaField.getType()).cfgImplClassName);
+ } else if (isNamedConfigValue(schemaField)) {
+ fieldType = type(NamedListConfiguration.class);
+ } else {
+ fieldType = type(DynamicProperty.class);
+ }
+
+ return cfgImplClassDef.declareField(EnumSet.of(PUBLIC), fieldName, fieldType);
+ }
+
+ /**
+ * Implements default constructor for the configuration class. It initializes all fields and adds them to members collection.
+ *
+ * @param fieldDefs Field definitions for all fields of configuration impl class.
+ * @param internalConfigTypesFieldDef Field definition for {@link DynamicConfiguration#internalConfigTypes},
+ * {@code null} if there are no internal extensions.
+ */
+ private void addConfigurationImplConstructor(
+ Map<String, FieldDefinition> fieldDefs,
+ @Nullable FieldDefinition internalConfigTypesFieldDef
+ ) {
+ MethodDefinition ctor = cfgImplClassDef.declareConstructor(
+ EnumSet.of(PUBLIC),
+ arg("prefix", List.class),
+ arg("key", String.class),
+ arg("rootKey", RootKey.class),
+ arg("changer", DynamicConfigurationChanger.class),
+ arg("listenOnly", boolean.class)
+ );
+
+ Variable rootKeyVar = ctor.getScope().getVariable("rootKey");
+ Variable changerVar = ctor.getScope().getVariable("changer");
+ Variable listenOnlyVar = ctor.getScope().getVariable("listenOnly");
+
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+
+ Variable thisVar = ctor.getThis();
+
+ BytecodeBlock ctorBody = ctor.getBody()
+ .append(thisVar)
+ .append(ctor.getScope().getVariable("prefix"))
+ .append(ctor.getScope().getVariable("key"))
+ .append(rootKeyVar)
+ .append(changerVar)
+ .append(listenOnlyVar)
+ .invokeConstructor(DYNAMIC_CONFIGURATION_CTOR);
+
+ BytecodeExpression thisKeysVar = thisVar.getField("keys", List.class);
+
+ // Wrap object into list to reuse the loop below.
+ List<Field> internalIdFieldAsList = internalIdField == null ? emptyList() : List.of(internalIdField);
+
+ int newIdx = 0;
+ for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields, internalIdFieldAsList)) {
+ String fieldName = schemaField.getName();
+
+ BytecodeExpression newValue;
+
+ if (isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField)) {
+ // A field with @InjectedName is special (auxiliary), it is not stored in storages as a regular field, and therefore there
+ // is no direct access to it. It is stored in the InnerNode and does not participate in its traversal, so in order to get
+ // it we need to get the InnerNode, and only then the value of this field.
+
+ // newValue = new DynamicProperty(this.keys, fieldName, rootKey, changer, listenOnly, readOnly);
+ newValue = newInstance(
+ DynamicProperty.class,
+ thisKeysVar,
+ constantString(isInjectedName(schemaField) ? InnerNode.INJECTED_NAME
+ : isInternalId(schemaField) ? InnerNode.INTERNAL_ID : schemaField.getName()),
+ rootKeyVar,
+ changerVar,
+ listenOnlyVar,
+ constantBoolean(isPolymorphicId(schemaField) || isInjectedName(schemaField) || isInternalId(schemaField))
+ );
+ } else {
+ SchemaClassesInfo fieldInfo = cgen.schemaInfo(schemaField.getType());
+
+ ParameterizedType cfgImplParameterizedType = typeFromJavaClassName(fieldInfo.cfgImplClassName);
+
+ if (isConfigValue(schemaField)) {
+ // newValue = new MyConfigurationImpl(super.keys, fieldName, rootKey, changer, listenOnly);
+ newValue = newInstance(
+ cfgImplParameterizedType,
+ thisKeysVar,
+ constantString(fieldName),
+ rootKeyVar,
+ changerVar,
+ listenOnlyVar
+ );
+ } else {
+ // We have to create method "$new$<idx>" to reference it in lambda expression. That's the way it
+ // works, it'll invoke constructor with all 5 arguments, not just 2 as in BiFunction.
+ MethodDefinition newMtd = cfgImplClassDef.declareMethod(
+ EnumSet.of(PRIVATE, STATIC, SYNTHETIC),
+ "$new$" + newIdx++,
+ typeFromJavaClassName(fieldInfo.cfgClassName),
+ arg("rootKey", RootKey.class),
+ arg("changer", DynamicConfigurationChanger.class),
+ arg("listenOnly", boolean.class),
+ arg("prefix", List.class),
+ arg("key", String.class)
+ );
+
+ // newValue = new NamedListConfiguration(this.keys, fieldName, rootKey, changer, listenOnly,
+ // (p, k) -> new ValueConfigurationImpl(p, k, rootKey, changer, listenOnly),
+ // (p, c) -> new ValueDirectProxy(p, c),
+ // new ValueConfigurationImpl(this.keys, "any", rootKey, changer, true)
+ // );
+ newValue = newInstance(
+ NamedListConfiguration.class,
+ thisKeysVar,
+ constantString(fieldName),
+ rootKeyVar,
+ changerVar,
+ listenOnlyVar,
+ invokeDynamic(
+ LAMBDA_METAFACTORY,
+ asList(
+ getMethodType(getType(Object.class), getType(Object.class), getType(Object.class)),
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ internalName(schemaClassInfo.cfgImplClassName),
+ newMtd.getName(),
+ newMtd.getMethodDescriptor(),
+ false
+ ),
+ getMethodType(
+ typeFromJavaClassName(fieldInfo.cfgClassName).getAsmType(),
+ getType(List.class),
+ getType(String.class)
+ )
+ ),
+ "apply",
+ BiFunction.class,
+ rootKeyVar,
+ changerVar,
+ listenOnlyVar
+ ),
+ newDirectProxyLambda(fieldInfo),
+ newInstance(
+ cfgImplParameterizedType,
+ thisKeysVar,
+ constantString("any"),
+ rootKeyVar,
+ changerVar,
+ constantBoolean(true)
+ ).cast(ConfigurationProperty.class)
+ );
+
+ newMtd.getBody()
+ .append(newInstance(
+ cfgImplParameterizedType,
+ newMtd.getScope().getVariable("prefix"),
+ newMtd.getScope().getVariable("key"),
+ newMtd.getScope().getVariable("rootKey"),
+ newMtd.getScope().getVariable("changer"),
+ newMtd.getScope().getVariable("listenOnly")
+ ))
+ .retObject();
+ }
+ }
+
+ FieldDefinition fieldDef = fieldDefs.get(fieldName(schemaField));
+
+ // this.field = newValue;
+ ctorBody.append(thisVar.setField(fieldDef, newValue));
+
+ if (!isPolymorphicConfigInstance(schemaField.getDeclaringClass()) && !isInternalId(schemaField)) {
+ // add(this.field);
+ ctorBody.append(thisVar.invoke(DYNAMIC_CONFIGURATION_ADD_MTD, thisVar.getField(fieldDef)));
+ }
+ }
+
+ if (internalConfigTypesFieldDef != null) {
+ assert !internalExtensions.isEmpty() : cfgImplClassDef;
+
+ // Class[] tmp;
+ Variable tmpVar = ctor.getScope().createTempVariable(Class[].class);
+
+ BytecodeBlock initInternalConfigTypesField = new BytecodeBlock();
+
+ // tmp = new Class[size];
+ initInternalConfigTypesField.append(tmpVar.set(newArray(type(Class[].class), internalExtensions.size())));
+
+ int i = 0;
+
+ for (Class<?> extension : internalExtensions) {
+ // tmp[i] = InternalTableConfiguration.class;
+ initInternalConfigTypesField.append(set(
+ tmpVar,
+ constantInt(i++),
+ constantClass(typeFromJavaClassName(configurationClassName(extension)))
+ ));
+ }
+
+ // this._internalConfigTypes = tmp;
+ initInternalConfigTypesField.append(setThisFieldCode(ctor, tmpVar, internalConfigTypesFieldDef));
+
+ ctorBody.append(initInternalConfigTypesField);
+ }
+
+ ctorBody.ret();
+ }
+
+ /**
+ * Generates {@link ConfigurationNode#directProxy()} method that returns new instance every time.
+ *
+ * @param schemaClassInfo Schema class info.
+ */
+ private void addDirectProxyMethod(SchemaClassesInfo schemaClassInfo) {
+ MethodDefinition methodDef = cfgImplClassDef.declareMethod(
+ EnumSet.of(PUBLIC), "directProxy", type(DirectPropertyProxy.class)
+ );
+
+ methodDef.getBody().append(newInstance(
+ typeFromJavaClassName(schemaClassInfo.directProxyClassName),
+ methodDef.getThis().invoke("keyPath", List.class),
+ methodDef.getThis().getField("changer", DynamicConfigurationChanger.class)
+ ));
+
+ methodDef.getBody().retObject();
+ }
+
+ /**
+ * Implements accessor method in configuration impl class.
+ *
+ * @param classDef Configuration impl class definition.
+ * @param schemaField Configuration Schema class field.
+ * @param fieldDefs A chain of field definitions to access. For example, for fields "a", "b" and "c" the access would be
+ * {@code this.a.b.c}.
+ */
+ private void addConfigurationImplGetMethod(
+ ClassDefinition classDef,
+ Field schemaField,
+ FieldDefinition... fieldDefs
+ ) {
+ assert !nullOrEmpty(fieldDefs);
+
+ Class<?> schemaFieldType = schemaField.getType();
+
+ String fieldName = schemaField.getName();
+
+ ParameterizedType returnType;
+
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaFieldType);
+
+ if (isConfigValue(schemaField)) {
+ returnType = typeFromJavaClassName(schemaClassInfo.cfgClassName);
+ } else if (isNamedConfigValue(schemaField)) {
+ returnType = type(NamedConfigurationTree.class);
+ } else {
+ assert isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField)
+ || isInternalId(schemaField) : schemaField;
+
+ returnType = type(ConfigurationValue.class);
+ }
+
+ // public ConfigurationProperty fieldName()
+ MethodDefinition viewMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ fieldName,
+ returnType
+ );
+
+ // result = this.field;
+ BytecodeBlock body = viewMtd.getBody().append(getThisFieldCode(viewMtd, fieldDefs));
+
+ if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
+ // result = this.field.specificConfigTree();
+ body.invokeVirtual(SPECIFIC_CONFIG_TREE_MTD);
+ }
+
+ // return result;
+ body.retObject();
+ }
+
+ /**
+ * Add {@link DynamicConfiguration#configType} method implementation to the class.
+ *
+ * <p>It looks like the following code:
+ * <pre><code>
+ * public Class configType() {
+ * return RootConfiguration.class;
+ * }
+ * </code></pre>
+ *
+ * @param clazz Definition of the configuration interface, for example {@code RootConfiguration}.
+ */
+ private void addCfgImplConfigTypeMethod(ParameterizedType clazz) {
+ cfgImplClassDef.declareMethod(EnumSet.of(PUBLIC), "configType", type(Class.class))
+ .getBody()
+ .append(constantClass(clazz))
+ .retObject();
+ }
+
+ /**
+ * Add {@link DynamicConfiguration#internalConfigTypes} method implementation to the class.
+ *
+ * <p>It looks like the following code:
+ * <pre><code>
+ * public Class<?>[] internalConfigTypes() {
+ * return new Class<?>[]{FirstInternalTableConfiguration.class, SecondInternalTableConfiguration.class};
+ * }
+ * </code></pre>
+ *
+ * @param classDef Class definition.
+ * @param internalConfigTypesDef Definition of the field in which the interfaces of the internal configuration extensions are stored.
+ */
+ private static void addCfgImplInternalConfigTypesMethod(ClassDefinition classDef, FieldDefinition internalConfigTypesDef) {
+ MethodDefinition internalConfigTypesMtd = classDef.declareMethod(EnumSet.of(PUBLIC), "internalConfigTypes", type(Class[].class));
+
+ internalConfigTypesMtd
+ .getBody()
+ .append(getThisFieldCode(internalConfigTypesMtd, internalConfigTypesDef))
+ .retObject();
+ }
+
+ /**
+ * Add {@link DynamicConfiguration#polymorphicInstanceConfigType} method implementation to the class.
+ *
+ * <p>It looks like the following code:
+ * <pre><code>
+ * public Class polymorphicInstanceConfigType() {
+ * InnerNode val = this.isRemovedFromNamedList() ? this.currentValue() : this.refreshValue();
+ * String typeId = val.polymorphicTypeId;
+ * switch(typeId) {
+ * case "hash":
+ * return HashIndexConfiguration.class;
+ * case "sorted"
+ * return SortedIndexConfiguration.class;
+ * default:
+ * throw new ConfigurationWrongPolymorphicTypeIdException(typeId);
+ * }
+ * }
+ * </code></pre>
+ *
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addCfgImplPolymorphicInstanceConfigTypeMethod(FieldDefinition polymorphicTypeIdFieldDef) {
+ MethodDefinition polymorphicInstanceConfigTypeMtd = cfgImplClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "polymorphicInstanceConfigType",
+ type(Class.class)
+ );
+
+ // String tmpStr;
+ Variable tmpStrVar = polymorphicInstanceConfigTypeMtd.getScope().createTempVariable(String.class);
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(polymorphicInstanceConfigTypeMtd.getScope())
+ .expression(tmpStrVar)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ switchBuilder.addCase(
+ polymorphicInstanceId(polymorphicExtension),
+ constantClass(typeFromJavaClassName(configurationClassName(polymorphicExtension))).ret()
+ );
+ }
+
+ // ConfigNode
+ ParameterizedType nodeType = typeFromJavaClassName(nodeClassName(schemaClass));
+
+ // Object tmpObj;
+ Variable tmpObjVar = polymorphicInstanceConfigTypeMtd.getScope().createTempVariable(Object.class);
+
+ // this;
+ Variable thisVar = polymorphicInstanceConfigTypeMtd.getThis();
+
+ // tmpObj = this.isRemovedFromNamedList() ? this.currentValue() : this.refreshValue();
+ // tmpStr = ((ConfigNode) tmpObj).typeId;
+ // switch(tmpStr) ...
+ polymorphicInstanceConfigTypeMtd.getBody()
+ .append(tmpObjVar.set(inlineIf(
+ thisVar.invoke(IS_REMOVED_FROM_NAMED_LIST_MTD),
+ thisVar.invoke(CURRENT_VALUE_MTD),
+ thisVar.invoke(REFRESH_VALUE_MTD))
+ ))
+ .append(tmpStrVar.set(tmpObjVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
+ .append(switchBuilder.build())
+ .ret();
+ }
+
+ /**
+ * Create a {@code *CfgImpl} for the polymorphic configuration instance schema.
+ *
+ * @param polymorphicExtension Polymorphic configuration instance schema (child).
+ * @param polymorphicFields Schema fields of a polymorphic configuration instance {@code polymorphicExtension}.
+ */
+ private ClassDefinition createPolymorphicExtensionCfgImplClass(
+ Class<?> polymorphicExtension,
+ Collection<Field> polymorphicFields
+ ) {
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+ SchemaClassesInfo polymorphicExtensionClassInfo = cgen.schemaInfo(polymorphicExtension);
+
+ // Configuration impl class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ EnumSet.of(PUBLIC, FINAL),
+ internalName(polymorphicExtensionClassInfo.cfgImplClassName),
+ type(ConfigurationTreeWrapper.class),
+ cgen.configClassInterfaces(polymorphicExtension, Set.of())
+ );
+
+ // private final ParentCfgImpl this$0;
+ FieldDefinition parentCfgImplFieldDef = classDef.declareField(
+ EnumSet.of(PRIVATE, FINAL),
+ "this$0",
+ typeFromJavaClassName(schemaClassInfo.cfgImplClassName)
+ );
+
+ // Constructor.
+ MethodDefinition constructorMtd = classDef.declareConstructor(
+ EnumSet.of(PUBLIC),
+ arg("delegate", typeFromJavaClassName(schemaClassInfo.cfgImplClassName))
+ );
+
+ Variable delegateVar = constructorMtd.getScope().getVariable("delegate");
+
+ // Constructor body.
+ // super(parent);
+ // this.this$0 = parent;
+ constructorMtd.getBody()
+ .append(constructorMtd.getThis())
+ .append(delegateVar)
+ .invokeConstructor(ConfigurationTreeWrapper.class, ConfigurationTree.class)
+ .append(constructorMtd.getThis().setField(
+ parentCfgImplFieldDef,
+ delegateVar
+ ))
+ .ret();
+
+ Map<String, FieldDefinition> fieldDefs = cfgImplClassDef.getFields().stream()
+ .collect(toMap(FieldDefinition::getName, identity()));
+
+ if (internalIdField != null) {
+ addConfigurationImplGetMethod(classDef, internalIdField, parentCfgImplFieldDef, fieldDefs.get(internalIdField.getName()));
+ }
+
+ for (Field schemaField : concat(schemaFields, polymorphicFields)) {
+ addConfigurationImplGetMethod(classDef, schemaField, parentCfgImplFieldDef, fieldDefs.get(fieldName(schemaField)));
+ }
+
+ return classDef;
+ }
+
+ /**
+ * Adds a {@link DynamicConfiguration#specificConfigTree} override for the polymorphic configuration case.
+ *
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addCfgSpecificConfigTreeMethod(FieldDefinition polymorphicTypeIdFieldDef) {
+ MethodDefinition specificConfigMtd = cfgImplClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ SPECIFIC_CONFIG_TREE_MTD.getName(),
+ type(ConfigurationTree.class)
+ );
+
+ // String tmpStr;
+ Variable tmpStrVar = specificConfigMtd.getScope().createTempVariable(String.class);
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(specificConfigMtd.getScope())
+ .expression(tmpStrVar)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ // return new SpecialCfgImpl(this);
+ switchBuilder.addCase(
+ polymorphicInstanceId(polymorphicExtension),
+ newInstance(
+ typeFromJavaClassName(cgen.schemaInfo(polymorphicExtension).cfgImplClassName),
+ specificConfigMtd.getThis()
+ ).ret()
+ );
+ }
+
+ // ConfigNode
+ ParameterizedType nodeType = typeFromJavaClassName(cgen.schemaInfo(schemaClass).nodeClassName);
+
+ // Object tmpObj;
+ Variable tmpObjVar = specificConfigMtd.getScope().createTempVariable(Object.class);
+
+ // tmpObj = this.refreshValue();
+ // tmpStr = ((ConfigNode) tmpObj).typeId;
+ // switch(tmpStr) ...
+ specificConfigMtd.getBody()
+ .append(tmpObjVar.set(specificConfigMtd.getThis().invoke(REFRESH_VALUE_MTD)))
+ .append(tmpStrVar.set(tmpObjVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
+ .append(switchBuilder.build())
+ .ret();
+ }
+
+ /**
+ * Adds a {@code DynamicConfiguration#removeMembers} override for the polymorphic configuration case.
+ *
+ * @param fieldDefs Field definitions for all fields of {@code cfgImplClassDef}.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addCfgRemoveMembersMethod(Map<String, FieldDefinition> fieldDefs, FieldDefinition polymorphicTypeIdFieldDef) {
+ MethodDefinition removeMembersMtd = cfgImplClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "removeMembers",
+ type(void.class),
+ arg("oldValue", type(Object.class)),
+ arg("members", type(Map.class))
+ );
+
+ // InnerNode oldValue;
+ Variable oldValueVar = removeMembersMtd.getScope().getVariable("oldValue");
+
+ // Map members;
+ Variable membersVar = removeMembersMtd.getScope().getVariable("members");
+
+ // String tmpStr;
+ Variable tmpStrVar = removeMembersMtd.getScope().createTempVariable(String.class);
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(removeMembersMtd.getScope())
+ .expression(tmpStrVar)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ Collection<Field> removeFields = polymorphicFields.stream()
+ .filter(f -> !polymorphicExtension.equals(f.getDeclaringClass()))
+ .collect(toList());
+
+ BytecodeBlock blockCode = new BytecodeBlock();
+
+ for (Field removeField : removeFields) {
+ // this.removeMember(members, this.field);
+ blockCode
+ .append(removeMembersMtd.getThis())
+ .append(membersVar)
+ .append(getThisFieldCode(removeMembersMtd, fieldDefs.get(fieldName(removeField))))
+ .invokeVirtual(REMOVE_MEMBER_MTD);
+ }
+
+ switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), blockCode);
+ }
+
+ // ConfigNode
+ ParameterizedType nodeType = typeFromJavaClassName(cgen.schemaInfo(schemaClass).nodeClassName);
+
+ // tmpStr = ((ConfigNode) oldValue).typeId;
+ // switch(tmpStr) ...
+ removeMembersMtd.getBody()
+ .append(tmpStrVar.set(oldValueVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
+ .append(switchBuilder.build())
+ .ret();
+ }
+
+ /**
+ * Adds a {@code DynamicConfiguration#addMembers} override for the polymorphic configuration case.
+ *
+ * @param fieldDefs Field definitions for all fields of {@code cfgImplClassDef}.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addCfgAddMembersMethod(
+ Map<String, FieldDefinition> fieldDefs,
+ FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition removeMembersMtd = cfgImplClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "addMembers",
+ type(void.class),
+ arg("newValue", type(Object.class)),
+ arg("members", type(Map.class))
+ );
+
+ // InnerNode newValue;
+ Variable newValueVar = removeMembersMtd.getScope().getVariable("newValue");
+
+ // Map members;
+ Variable membersVar = removeMembersMtd.getScope().getVariable("members");
+
+ // String tmpStr;
+ Variable tmpStrVar = removeMembersMtd.getScope().createTempVariable(String.class);
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(removeMembersMtd.getScope())
+ .expression(tmpStrVar)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, tmpStrVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ Collection<Field> addFields = polymorphicFields.stream()
+ .filter(f -> polymorphicExtension.equals(f.getDeclaringClass()))
+ .collect(toList());
+
+ BytecodeBlock blockCode = new BytecodeBlock();
+
+ for (Field addField : addFields) {
+ // this.addMember(members, this.field);
+ blockCode
+ .append(removeMembersMtd.getThis())
+ .append(membersVar)
+ .append(getThisFieldCode(removeMembersMtd, fieldDefs.get(fieldName(addField))))
+ .invokeVirtual(ADD_MEMBER_MTD);
+ }
+
+ switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), blockCode);
+ }
+
+ // ConfigNode
+ ParameterizedType nodeType = typeFromJavaClassName(cgen.schemaInfo(schemaClass).nodeClassName);
+
+ // tmpStr = ((ConfigNode) newValue).typeId;
+ // switch(tmpStr) ...
+ removeMembersMtd.getBody()
+ .append(tmpStrVar.set(newValueVar.cast(nodeType).getField(polymorphicTypeIdFieldDef.getName(), String.class)))
+ .append(switchBuilder.build())
+ .ret();
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/DirectProxyAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/DirectProxyAsmGenerator.java
index 7d4121abdb..e22762b202 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/DirectProxyAsmGenerator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/DirectProxyAsmGenerator.java
@@ -28,8 +28,6 @@ import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invoke
import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newInstance;
import static java.lang.invoke.MethodType.methodType;
import static java.util.Arrays.asList;
-import static java.util.EnumSet.of;
-import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.LAMBDA_METAFACTORY;
import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.internalName;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isConfigValue;
import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInjectedName;
@@ -53,12 +51,12 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import org.apache.ignite.configuration.ConfigurationValue;
import org.apache.ignite.configuration.NamedConfigurationTree;
-import org.apache.ignite.configuration.annotation.InternalId;
import org.apache.ignite.internal.configuration.DynamicConfigurationChanger;
import org.apache.ignite.internal.configuration.direct.DirectConfigurationProxy;
import org.apache.ignite.internal.configuration.direct.DirectNamedListProxy;
@@ -74,45 +72,26 @@ import org.objectweb.asm.Handle;
* Helper class to generate classes that extend {@link DirectConfigurationProxy}.
* All that's required here is to generate constructor and a bunch of getter methods.
*/
-class DirectProxyAsmGenerator {
+class DirectProxyAsmGenerator extends AbstractAsmGenerator {
/** {@link DirectConfigurationProxy#DirectConfigurationProxy(List, DynamicConfigurationChanger)}. */
private static final Constructor<?> DIRECT_CFG_CTOR;
/** {@link ConfigurationUtil#appendKey(List, Object)}. */
private static final Method APPEND_KEY;
- /** This generator instance is only used for {@link ConfigurationAsmGenerator#schemaInfo(java.lang.Class)}. */
- private final ConfigurationAsmGenerator cgen;
-
- /** Schema class. */
- private final Class<?> schemaClass;
-
- /** Set of internal extensions. */
- private final Set<Class<?>> internalExtensions;
-
- /** Fields from the schema class. */
- private final List<Field> schemaFields;
-
- /** Fields from all internal extensions. */
- private final Collection<Field> internalExtensionsFields;
-
- /** {@link InternalId} field. */
- @Nullable
- private final Field internalIdField;
-
- /** Class definition that extends the {@link DirectConfigurationProxy}. */
- private ClassDefinition classDef;
-
static {
try {
DIRECT_CFG_CTOR = DirectConfigurationProxy.class.getDeclaredConstructor(List.class, DynamicConfigurationChanger.class);
APPEND_KEY = ConfigurationUtil.class.getDeclaredMethod("appendKey", List.class, Object.class);
- } catch (NoSuchMethodException e) {
- throw new ExceptionInInitializerError(e);
+ } catch (NoSuchMethodException nsme) {
+ throw new ExceptionInInitializerError(nsme);
}
}
+ /** Class definition that extends the {@link DirectConfigurationProxy}. */
+ private ClassDefinition classDef;
+
/**
* Constructor.
* Please refer to individual fields for comments.
@@ -123,27 +102,29 @@ class DirectProxyAsmGenerator {
Set<Class<?>> internalExtensions,
List<Field> schemaFields,
Collection<Field> internalExtensionsFields,
- Field internalIdField
+ @Nullable Field internalIdField
) {
- this.cgen = cgen;
- this.schemaClass = schemaClass;
- this.internalExtensions = internalExtensions;
- this.schemaFields = schemaFields;
- this.internalExtensionsFields = internalExtensionsFields;
- this.internalIdField = internalIdField;
+ super(
+ cgen,
+ schemaClass,
+ internalExtensions,
+ null,
+ schemaFields,
+ internalExtensionsFields,
+ null,
+ internalIdField
+ );
}
- /**
- * Generates class definition. Expected to be called once at most.
- */
- public ClassDefinition generate() {
+ @Override
+ public List<ClassDefinition> generate() {
assert classDef == null;
SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
// public final class FooDirectProxy extends DirectConfigurationProxy<Object, Object> implements FooConfiguration, ...
classDef = new ClassDefinition(
- of(PUBLIC, FINAL),
+ EnumSet.of(PUBLIC, FINAL),
internalName(schemaClassInfo.directProxyClassName),
type(DirectConfigurationProxy.class),
cgen.configClassInterfaces(schemaClass, internalExtensions)
@@ -155,11 +136,11 @@ class DirectProxyAsmGenerator {
addGetMethod(internalIdField);
}
- for (Field schemaField : concat(schemaFields, internalExtensionsFields)) {
+ for (Field schemaField : concat(schemaFields, internalFields)) {
addGetMethod(schemaField);
}
- return classDef;
+ return List.of(classDef);
}
/**
@@ -168,7 +149,7 @@ class DirectProxyAsmGenerator {
private void addConstructor() {
// public FooDirectProxy(List<KeyPathNode> keys, DynamicConfigurationChanger changer) {
MethodDefinition ctor = classDef.declareConstructor(
- of(PUBLIC),
+ EnumSet.of(PUBLIC),
arg("keys", List.class),
arg("changer", DynamicConfigurationChanger.class)
);
@@ -208,7 +189,7 @@ class DirectProxyAsmGenerator {
}
MethodDefinition methodDef = classDef.declareMethod(
- of(PUBLIC),
+ EnumSet.of(PUBLIC),
fieldName,
returnType
);
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
new file mode 100644
index 0000000000..94afc231dc
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
@@ -0,0 +1,1890 @@
+/*
+ * 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.ignite.internal.configuration.asm;
+
+import static com.facebook.presto.bytecode.Access.BRIDGE;
+import static com.facebook.presto.bytecode.Access.FINAL;
+import static com.facebook.presto.bytecode.Access.PRIVATE;
+import static com.facebook.presto.bytecode.Access.PUBLIC;
+import static com.facebook.presto.bytecode.Access.SYNTHETIC;
+import static com.facebook.presto.bytecode.Parameter.arg;
+import static com.facebook.presto.bytecode.ParameterizedType.type;
+import static com.facebook.presto.bytecode.ParameterizedType.typeFromJavaClassName;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantClass;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantNull;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantString;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.inlineIf;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.invokeStatic;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.isNotNull;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.isNull;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newArray;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.newInstance;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.not;
+import static com.facebook.presto.bytecode.expression.BytecodeExpressions.set;
+import static java.util.Collections.singleton;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.box;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.changeMethodName;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.fieldName;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.getThisFieldCode;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.internalName;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.nodeClassInterfaces;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.polymorphicIdField;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.setThisFieldCode;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.throwException;
+import static org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.typeIdSwitchBuilder;
+import static org.apache.ignite.internal.configuration.asm.SchemaClassesInfo.changeClassName;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.containsNameAnnotation;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.hasDefault;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isInjectedName;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isNamedConfigValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfig;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicConfigInstance;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicId;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.isValue;
+import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.polymorphicInstanceId;
+import static org.apache.ignite.internal.util.CollectionUtils.concat;
+
+import com.facebook.presto.bytecode.BytecodeBlock;
+import com.facebook.presto.bytecode.BytecodeNode;
+import com.facebook.presto.bytecode.ClassDefinition;
+import com.facebook.presto.bytecode.FieldDefinition;
+import com.facebook.presto.bytecode.MethodDefinition;
+import com.facebook.presto.bytecode.ParameterizedType;
+import com.facebook.presto.bytecode.Variable;
+import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.expression.BytecodeExpression;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdException;
+import org.apache.ignite.configuration.NamedListView;
+import org.apache.ignite.configuration.annotation.AbstractConfiguration;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.configuration.annotation.Name;
+import org.apache.ignite.configuration.annotation.PolymorphicConfig;
+import org.apache.ignite.configuration.annotation.PolymorphicId;
+import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
+import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
+import org.apache.ignite.internal.configuration.tree.ConstructableTreeNode;
+import org.apache.ignite.internal.configuration.tree.InnerNode;
+import org.apache.ignite.internal.configuration.tree.NamedListNode;
+import org.apache.ignite.internal.configuration.util.ConfigurationUtil;
+import org.apache.ignite.internal.util.ArrayUtils;
+import org.jetbrains.annotations.Nullable;
+
+//TODO Simplify code generation process: https://issues.apache.org/jira/browse/IGNITE-18366
+class InnerNodeAsmGenerator extends AbstractAsmGenerator {
+ /** {@link Consumer#accept(Object)}. */
+ private static final Method ACCEPT;
+
+ /** {@link ConfigurationVisitor#visitLeafNode(String, Serializable)}. */
+ private static final Method VISIT_LEAF;
+
+ /** {@link ConfigurationVisitor#visitInnerNode(String, InnerNode)}. */
+ private static final Method VISIT_INNER;
+
+ /** {@link ConfigurationVisitor#visitNamedListNode(String, NamedListNode)}. */
+ private static final Method VISIT_NAMED;
+
+ /** {@link ConfigurationSource#unwrap(Class)}. */
+ private static final Method UNWRAP;
+
+ /** {@link ConfigurationSource#descend(ConstructableTreeNode)}. */
+ private static final Method DESCEND;
+
+ /** {@link InnerNode#internalId()}. */
+ private static final Method INTERNAL_ID;
+
+ /** {@link Objects#requireNonNull(Object, String)}. */
+ private static final Method REQUIRE_NON_NULL;
+
+ /** {@link Class#getName} method. */
+ private static final Method CLASS_GET_NAME_MTD;
+
+ /** {@link String#equals} method. */
+ private static final Method STRING_EQUALS_MTD;
+
+ /** {@link ConfigurationSource#polymorphicTypeId} method. */
+ private static final Method POLYMORPHIC_TYPE_ID_MTD;
+
+ /** {@link InnerNode#constructDefault} method. */
+ private static final Method CONSTRUCT_DEFAULT_MTD;
+
+ /** {@link InnerNode#specificNode} method. */
+ private static final Method SPECIFIC_NODE_MTD;
+
+ /** {@link ConfigurationUtil#addDefaults}. */
+ private static final Method ADD_DEFAULTS_MTD;
+
+ /** {@link InnerNode#setInjectedNameFieldValue}. */
+ private static final Method SET_INJECTED_NAME_FIELD_VALUE_MTD;
+
+ /** {@link InnerNode#isPolymorphic}. */
+ private static final Method IS_POLYMORPHIC_MTD;
+
+ /** {@link InnerNode#internalSchemaTypes}. */
+ private static final Method INTERNAL_SCHEMA_TYPES_MTD;
+
+ /** {@code Node#convert} method name. */
+ private static final String CONVERT_MTD_NAME = "convert";
+
+ /** {@link ConstructableTreeNode#construct(String, ConfigurationSource, boolean)} method name. */
+ private static final String CONSTRUCT_MTD_NAME = "construct";
+
+ static {
+ try {
+ ACCEPT = Consumer.class.getDeclaredMethod("accept", Object.class);
+
+ VISIT_LEAF = ConfigurationVisitor.class
+ .getDeclaredMethod("visitLeafNode", String.class, Serializable.class);
+
+ VISIT_INNER = ConfigurationVisitor.class
+ .getDeclaredMethod("visitInnerNode", String.class, InnerNode.class);
+
+ VISIT_NAMED = ConfigurationVisitor.class
+ .getDeclaredMethod("visitNamedListNode", String.class, NamedListNode.class);
+
+ UNWRAP = ConfigurationSource.class.getDeclaredMethod("unwrap", Class.class);
+
+ DESCEND = ConfigurationSource.class.getDeclaredMethod("descend", ConstructableTreeNode.class);
+
+ INTERNAL_ID = InnerNode.class.getDeclaredMethod("internalId");
+
+ REQUIRE_NON_NULL = Objects.class.getDeclaredMethod("requireNonNull", Object.class, String.class);
+
+ CLASS_GET_NAME_MTD = Class.class.getDeclaredMethod("getName");
+
+ STRING_EQUALS_MTD = String.class.getDeclaredMethod("equals", Object.class);
+
+ POLYMORPHIC_TYPE_ID_MTD = ConfigurationSource.class.getDeclaredMethod("polymorphicTypeId", String.class);
+
+ CONSTRUCT_DEFAULT_MTD = InnerNode.class.getDeclaredMethod("constructDefault", String.class);
+
+ SPECIFIC_NODE_MTD = InnerNode.class.getDeclaredMethod("specificNode");
+
+ ADD_DEFAULTS_MTD = ConfigurationUtil.class.getDeclaredMethod("addDefaults", InnerNode.class);
+
+ SET_INJECTED_NAME_FIELD_VALUE_MTD = InnerNode.class.getDeclaredMethod("setInjectedNameFieldValue", String.class);
+
+ IS_POLYMORPHIC_MTD = InnerNode.class.getDeclaredMethod("isPolymorphic");
+
+ INTERNAL_SCHEMA_TYPES_MTD = InnerNode.class.getDeclaredMethod("internalSchemaTypes");
+
+ } catch (NoSuchMethodException nsme) {
+ throw new ExceptionInInitializerError(nsme);
+ }
+ }
+
+ /** Class definition that extends the {@link InnerNode}. */
+ private ClassDefinition innerNodeClassDef;
+
+ InnerNodeAsmGenerator(
+ ConfigurationAsmGenerator cgen,
+ Class<?> schemaClass,
+ Set<Class<?>> internalExtensions,
+ Set<Class<?>> polymorphicExtensions,
+ List<Field> schemaFields,
+ Collection<Field> internalFields,
+ Collection<Field> polymorphicFields,
+ @Nullable Field internalIdField
+ ) {
+ super(
+ cgen,
+ schemaClass,
+ internalExtensions,
+ polymorphicExtensions,
+ schemaFields,
+ internalFields,
+ polymorphicFields,
+ internalIdField
+ );
+ }
+
+ @Override
+ public List<ClassDefinition> generate() {
+ assert innerNodeClassDef == null;
+
+ List<ClassDefinition> classDefs = new ArrayList<>();
+
+ classDefs.add(createNodeClass());
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ // Only the fields of a specific instance of a polymorphic configuration.
+ Collection<Field> polymorphicFields = this.polymorphicFields.stream()
+ .filter(f -> f.getDeclaringClass() == polymorphicExtension)
+ .collect(toList());
+
+ classDefs.add(createPolymorphicExtensionNodeClass(polymorphicExtension, polymorphicFields));
+ }
+
+ return classDefs;
+ }
+
+
+ /**
+ * Construct a {@link InnerNode} definition for a configuration schema.
+ *
+ * @return Constructed {@link InnerNode} definition for the configuration schema.
+ */
+ private ClassDefinition createNodeClass() {
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+
+ // Node class definition.
+ innerNodeClassDef = new ClassDefinition(
+ EnumSet.of(PUBLIC, FINAL),
+ internalName(schemaClassInfo.nodeClassName),
+ type(InnerNode.class),
+ nodeClassInterfaces(schemaClass, internalExtensions)
+ );
+
+ // Spec fields.
+ Map<Class<?>, FieldDefinition> specFields = new HashMap<>();
+
+ int i = 0;
+
+ for (Class<?> clazz : concat(List.of(schemaClass), internalExtensions, polymorphicExtensions)) {
+ specFields.put(clazz, innerNodeClassDef.declareField(EnumSet.of(PRIVATE, FINAL), "_spec" + i++, clazz));
+ }
+
+ // Define the rest of the fields.
+ Map<String, FieldDefinition> fieldDefs = new HashMap<>();
+
+ // To store the id of the polymorphic configuration instance.
+ FieldDefinition polymorphicTypeIdFieldDef = null;
+
+ // Field with @InjectedName.
+ FieldDefinition injectedNameFieldDef = null;
+
+ for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
+ FieldDefinition fieldDef = addInnerNodeField(schemaField);
+
+ fieldDefs.put(fieldDef.getName(), fieldDef);
+
+ if (isPolymorphicId(schemaField)) {
+ polymorphicTypeIdFieldDef = fieldDef;
+ } else if (isInjectedName(schemaField)) {
+ injectedNameFieldDef = fieldDef;
+ }
+ }
+
+ // org.apache.ignite.internal.configuration.tree.InnerNode#schemaType
+ addNodeSchemaTypeMethod(polymorphicTypeIdFieldDef);
+
+ FieldDefinition internalSchemaTypesFieldDef = null;
+
+ if (!internalExtensions.isEmpty()) {
+ internalSchemaTypesFieldDef = innerNodeClassDef.declareField(
+ EnumSet.of(PRIVATE, FINAL),
+ "_" + INTERNAL_SCHEMA_TYPES_MTD.getName(),
+ Class[].class
+ );
+ }
+
+ // Constructor.
+ addNodeConstructor(
+ specFields,
+ fieldDefs,
+ internalSchemaTypesFieldDef
+ );
+
+ // Add view method for internal id.
+ if (internalIdField != null) {
+ addNodeInternalIdMethod();
+ }
+
+ // VIEW and CHANGE methods.
+ for (Field schemaField : concat(schemaFields, internalFields)) {
+ String fieldName = schemaField.getName();
+
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ addNodeViewMethod(
+ innerNodeClassDef,
+ schemaField,
+ viewMtd -> getThisFieldCode(viewMtd, fieldDef),
+ null
+ );
+
+ // Read only.
+ if (isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
+ continue;
+ }
+
+ // Add change methods.
+ MethodDefinition changeMtd0 = addNodeChangeMethod(
+ innerNodeClassDef,
+ schemaField,
+ changeMtd -> getThisFieldCode(changeMtd, fieldDef),
+ (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, fieldDef),
+ null
+ );
+
+ addNodeChangeBridgeMethod(innerNodeClassDef, changeClassName(schemaField.getDeclaringClass()), changeMtd0);
+ }
+
+ Map<Class<?>, List<Field>> polymorphicFieldsByExtension = Map.of();
+
+ MethodDefinition changePolymorphicTypeIdMtd = null;
+
+ if (!polymorphicExtensions.isEmpty()) {
+ assert polymorphicTypeIdFieldDef != null : schemaClass.getName();
+
+ addNodeSpecificNodeMethod(polymorphicTypeIdFieldDef);
+
+ changePolymorphicTypeIdMtd = addNodeChangePolymorphicTypeIdMethod(fieldDefs, polymorphicTypeIdFieldDef);
+
+ addNodeConvertMethods(changePolymorphicTypeIdMtd);
+
+ polymorphicFieldsByExtension = new LinkedHashMap<>();
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ polymorphicFieldsByExtension.put(
+ polymorphicExtension,
+ polymorphicFields.stream()
+ .filter(f -> polymorphicExtension.equals(f.getDeclaringClass()))
+ .collect(toList())
+ );
+ }
+ }
+
+ // traverseChildren
+ addNodeTraverseChildrenMethod(
+ fieldDefs,
+ polymorphicFieldsByExtension,
+ polymorphicTypeIdFieldDef
+ );
+
+ // traverseChild
+ addNodeTraverseChildMethod(
+ fieldDefs,
+ polymorphicFieldsByExtension,
+ polymorphicTypeIdFieldDef
+ );
+
+ // construct
+ addNodeConstructMethod(
+ fieldDefs,
+ polymorphicFieldsByExtension,
+ polymorphicTypeIdFieldDef,
+ changePolymorphicTypeIdMtd
+ );
+
+ // constructDefault
+ addNodeConstructDefaultMethod(
+ specFields,
+ fieldDefs,
+ polymorphicFieldsByExtension,
+ polymorphicTypeIdFieldDef
+ );
+
+ if (injectedNameFieldDef != null) {
+ addInjectedNameFieldMethods(injectedNameFieldDef);
+ }
+
+ if (polymorphicTypeIdFieldDef != null) {
+ addIsPolymorphicMethod();
+ }
+
+ if (internalSchemaTypesFieldDef != null) {
+ addInternalSchemaTypesMethod(internalSchemaTypesFieldDef);
+ }
+
+ if (schemaClass.getSuperclass().isAnnotationPresent(AbstractConfiguration.class)) {
+ addIsExtendAbstractConfigurationMethod();
+ }
+
+ return innerNodeClassDef;
+ }
+
+ /**
+ * Add {@link InnerNode#schemaType} method implementation to the class.
+ *
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addNodeSchemaTypeMethod(@Nullable FieldDefinition polymorphicTypeIdFieldDef) {
+ MethodDefinition schemaTypeMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "schemaType",
+ type(Class.class)
+ );
+
+ BytecodeBlock mtdBody = schemaTypeMtd.getBody();
+
+ if (polymorphicExtensions.isEmpty()) {
+ mtdBody.append(constantClass(schemaClass)).retObject();
+ } else {
+ assert polymorphicTypeIdFieldDef != null : innerNodeClassDef.getName();
+
+ StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(schemaTypeMtd, polymorphicTypeIdFieldDef);
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ switchBuilderTypeId.addCase(
+ polymorphicInstanceId(polymorphicExtension),
+ constantClass(polymorphicExtension).ret()
+ );
+ }
+
+ mtdBody.append(switchBuilderTypeId.build());
+ }
+ }
+
+ /**
+ * Declares a field that corresponds to configuration value. Depending on the schema, 5 options are possible:
+ * <ul>
+ * <li>
+ * {@code @Value public type fieldName}<br/>becomes<br/>
+ * {@code public BoxedType fieldName}
+ * </li>
+ * <li>
+ * {@code @ConfigValue public MyConfigurationSchema fieldName}<br/>becomes<br/>
+ * {@code public MyNode fieldName}
+ * </li>
+ * <li>
+ * {@code @NamedConfigValue public type fieldName}<br/>becomes<br/>
+ * {@code public NamedListNode fieldName}
+ * </li>
+ * <li>
+ * {@code @PolymorphicId public String fieldName}<br/>becomes<br/>
+ * {@code public String fieldName}
+ * </li>
+ * <li>
+ * {@code @InjectedName public String fieldName}<br/>becomes<br/>
+ * {@code public String fieldName}
+ * </li>
+ * </ul>
+ *
+ * @param schemaField Configuration Schema class field.
+ * @return Declared field definition.
+ * @throws IllegalArgumentException If an unsupported {@code schemaField} was passed.
+ */
+ private FieldDefinition addInnerNodeField(Field schemaField) {
+ String fieldName = fieldName(schemaField);
+
+ Class<?> schemaFieldClass = schemaField.getType();
+
+ ParameterizedType nodeFieldType;
+
+ if (isValue(schemaField) || isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
+ nodeFieldType = type(box(schemaFieldClass));
+ } else if (isConfigValue(schemaField)) {
+ nodeFieldType = typeFromJavaClassName(cgen.schemaInfo(schemaFieldClass).nodeClassName);
+ } else if (isNamedConfigValue(schemaField)) {
+ nodeFieldType = type(NamedListNode.class);
+ } else {
+ throw new IllegalArgumentException("Unsupported field: " + schemaField);
+ }
+
+ return innerNodeClassDef.declareField(EnumSet.of(PUBLIC), fieldName, nodeFieldType);
+ }
+
+ /**
+ * Implements default constructor for the node class. It initializes {@code _spec} field and every other field that represents named
+ * list configuration.
+ *
+ * @param specFields Definition of fields for the {@code _spec#} fields of the node class. Mapping: configuration schema class -> {@code
+ * _spec#} field.
+ * @param fieldDefs Field definitions for all fields of node class excluding {@code _spec}.
+ * @param internalSchemaTypesFieldDef Final field which stores {@code internalExtensions}.
+ */
+ private void addNodeConstructor(
+ Map<Class<?>, FieldDefinition> specFields,
+ Map<String, FieldDefinition> fieldDefs,
+ @Nullable FieldDefinition internalSchemaTypesFieldDef
+ ) {
+ MethodDefinition ctor = innerNodeClassDef.declareConstructor(EnumSet.of(PUBLIC));
+
+ BytecodeBlock ctorBody = ctor.getBody();
+
+ // super();
+ ctorBody
+ .append(ctor.getThis())
+ .invokeConstructor(InnerNode.class);
+
+ // this._spec# = new MyConfigurationSchema();
+ for (Map.Entry<Class<?>, FieldDefinition> e : specFields.entrySet()) {
+ ctorBody.append(ctor.getThis().setField(e.getValue(), newInstance(e.getKey())));
+ }
+
+ for (Field schemaField : concat(schemaFields, internalFields, polymorphicFields)) {
+ if (!isNamedConfigValue(schemaField)) {
+ continue;
+ }
+
+ FieldDefinition fieldDef = fieldDefs.get(fieldName(schemaField));
+
+ // this.values = new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName");
+ ctorBody.append(setThisFieldCode(ctor, cgen.newNamedListNode(schemaField), fieldDef));
+ }
+
+ if (!internalExtensions.isEmpty()) {
+ assert internalSchemaTypesFieldDef != null : innerNodeClassDef;
+
+ // Class[] tmp;
+ Variable tmpVar = ctor.getScope().createTempVariable(Class[].class);
+
+ BytecodeBlock initInternalSchemaTypesField = new BytecodeBlock();
+
+ // tmp = new Class[size];
+ initInternalSchemaTypesField.append(tmpVar.set(newArray(type(Class[].class), internalExtensions.size())));
+
+ int i = 0;
+
+ for (Class<?> extension : internalExtensions) {
+ // tmp[i] = InternalTableConfigurationSchema.class;
+ initInternalSchemaTypesField.append(set(
+ tmpVar,
+ constantInt(i++),
+ constantClass(extension)
+ ));
+ }
+
+ // this._internalConfigTypes = tmp;
+ initInternalSchemaTypesField.append(setThisFieldCode(ctor, tmpVar, internalSchemaTypesFieldDef));
+
+ ctorBody.append(initInternalSchemaTypesField);
+ }
+
+ // return;
+ ctorBody.ret();
+ }
+
+ /**
+ * Generates method with the same name as the {@link #internalIdField} field, that calls {@link InnerNode#internalId()}.
+ */
+ private void addNodeInternalIdMethod() {
+ MethodDefinition internalIdMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ internalIdField.getName(),
+ type(UUID.class)
+ );
+
+ // return this.internalId();
+ internalIdMtd.getBody().append(internalIdMtd.getThis().invoke(INTERNAL_ID)).retObject();
+ }
+
+ /**
+ * Implements getter method from {@code VIEW} interface. It returns field value, possibly unboxed or cloned, depending on type.
+ *
+ * @param classDef Node class definition.
+ * @param schemaField Configuration Schema class field.
+ * @param getFieldCodeFun Function for creating bytecode to get a field, for example: {@code this.field} or {@code
+ * this.field.field}.
+ * @param getPolymorphicTypeIdFieldFun Function for creating bytecode to get the field that stores the identifier of the polymorphic
+ * configuration instance is needed to add a polymorphicTypeId check, for example: {@code
+ * this.typeId} or {@code this.field.typeId}.
+ */
+ private void addNodeViewMethod(
+ ClassDefinition classDef,
+ Field schemaField,
+ Function<MethodDefinition, BytecodeExpression> getFieldCodeFun,
+ @Nullable Function<MethodDefinition, BytecodeExpression> getPolymorphicTypeIdFieldFun
+ ) {
+ Class<?> schemaFieldType = schemaField.getType();
+
+ ParameterizedType returnType;
+
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaFieldType);
+
+ // Return type is either corresponding VIEW type or the same type as declared in schema.
+ if (isConfigValue(schemaField)) {
+ returnType = typeFromJavaClassName(schemaClassInfo.viewClassName);
+ } else if (isNamedConfigValue(schemaField)) {
+ returnType = type(NamedListView.class);
+ } else {
+ returnType = type(schemaFieldType);
+ }
+
+ String fieldName = schemaField.getName();
+
+ MethodDefinition viewMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ fieldName,
+ returnType
+ );
+
+ BytecodeBlock bytecodeBlock = new BytecodeBlock();
+
+ // result = this.field; OR this.field.field.
+ bytecodeBlock.append(getFieldCodeFun.apply(viewMtd));
+
+ if (schemaFieldType.isPrimitive()) {
+ // result = Box.boxValue(result); // Unboxing.
+ bytecodeBlock.invokeVirtual(
+ box(schemaFieldType),
+ schemaFieldType.getSimpleName() + "Value",
+ schemaFieldType
+ );
+ } else if (schemaFieldType.isArray()) {
+ // result = result.clone();
+ bytecodeBlock.invokeVirtual(schemaFieldType, "clone", Object.class).checkCast(schemaFieldType);
+ } else if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
+ // result = result.specificNode();
+ bytecodeBlock.invokeVirtual(SPECIFIC_NODE_MTD);
+ }
+
+ // return result;
+ bytecodeBlock.ret(schemaFieldType);
+
+ if (getPolymorphicTypeIdFieldFun != null) {
+ assert isPolymorphicConfigInstance(schemaField.getDeclaringClass()) : schemaField;
+
+ // tmpVar = this.typeId; OR this.field.typeId.
+ BytecodeExpression getPolymorphicTypeIdFieldValue = getPolymorphicTypeIdFieldFun.apply(viewMtd);
+ String polymorphicInstanceId = polymorphicInstanceId(schemaField.getDeclaringClass());
+
+ // if (!"first".equals(tmpVar)) throw Ex;
+ // else return value;
+ viewMtd.getBody().append(
+ new IfStatement()
+ .condition(not(constantString(polymorphicInstanceId).invoke(STRING_EQUALS_MTD, getPolymorphicTypeIdFieldValue)))
+ .ifTrue(throwException(ConfigurationWrongPolymorphicTypeIdException.class, getPolymorphicTypeIdFieldValue))
+ .ifFalse(bytecodeBlock)
+ );
+ } else {
+ viewMtd.getBody().append(bytecodeBlock);
+ }
+ }
+
+ /**
+ * Implements changer method from {@code CHANGE} interface.
+ *
+ * @param classDef Node class definition.
+ * @param schemaField Configuration schema class field.
+ * @return Definition of change method.
+ */
+ private MethodDefinition addNodeChangeMethod(
+ ClassDefinition classDef,
+ Field schemaField,
+ Function<MethodDefinition, BytecodeExpression> getFieldCodeFun,
+ BiFunction<MethodDefinition, BytecodeExpression, BytecodeExpression> setFieldCodeFun,
+ @Nullable Function<MethodDefinition, BytecodeExpression> getPolymorphicTypeIdFieldFun
+ ) {
+ Class<?> schemaFieldType = schemaField.getType();
+
+ MethodDefinition changeMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ changeMethodName(schemaField.getName()),
+ classDef.getType(),
+ // Change argument type is a Consumer for all inner or named fields.
+ arg("change", isValue(schemaField) ? type(schemaFieldType) : type(Consumer.class))
+ );
+
+ // var change;
+ BytecodeExpression changeVar = changeMtd.getScope().getVariable("change");
+
+ BytecodeBlock bytecodeBlock = new BytecodeBlock();
+
+ if (!schemaFieldType.isPrimitive()) {
+ // Objects.requireNonNull(newValue, "change");
+ bytecodeBlock.append(invokeStatic(REQUIRE_NON_NULL, changeVar, constantString("change")));
+ }
+
+ if (isValue(schemaField)) {
+ BytecodeExpression newValue;
+
+ if (schemaFieldType.isPrimitive()) {
+ ParameterizedType type = type(box(schemaFieldType));
+
+ // newValue = Box.valueOf(newValue); // Boxing.
+ newValue = invokeStatic(type, "valueOf", type, singleton(changeVar));
+ } else if (schemaFieldType.isArray()) {
+ // newValue = newValue.clone();
+ newValue = changeVar.invoke("clone", Object.class).cast(schemaFieldType);
+ } else {
+ newValue = changeVar;
+ }
+
+ // this.field = newValue;
+ bytecodeBlock.append(setFieldCodeFun.apply(changeMtd, newValue));
+ } else {
+ BytecodeExpression newValue;
+
+ if (isConfigValue(schemaField)) {
+ // newValue = (this.field == null) ? new ValueNode() : (ValueNode)this.field.copy();
+ newValue = cgen.newOrCopyNodeField(schemaField, getFieldCodeFun.apply(changeMtd));
+ } else {
+ assert isNamedConfigValue(schemaField) : schemaField;
+
+ // newValue = (ValueNode)this.field.copy();
+ newValue = cgen.copyNodeField(schemaField, getFieldCodeFun.apply(changeMtd));
+ }
+
+ // this.field = newValue;
+ bytecodeBlock.append(setFieldCodeFun.apply(changeMtd, newValue));
+
+ // this.field;
+ BytecodeExpression getFieldCode = getFieldCodeFun.apply(changeMtd);
+
+ if (isPolymorphicConfig(schemaFieldType) && isConfigValue(schemaField)) {
+ // this.field.specificNode();
+ getFieldCode = getFieldCode.invoke(SPECIFIC_NODE_MTD);
+ }
+
+ // change.accept(this.field); OR change.accept(this.field.specificNode());
+ bytecodeBlock.append(changeVar.invoke(ACCEPT, getFieldCode));
+ }
+
+ // return this;
+ bytecodeBlock.append(changeMtd.getThis()).retObject();
+
+ if (getPolymorphicTypeIdFieldFun != null) {
+ assert isPolymorphicConfigInstance(schemaField.getDeclaringClass()) : schemaField;
+
+ // tmpVar = this.typeId; OR this.field.typeId.
+ BytecodeExpression getPolymorphicTypeIdFieldValue = getPolymorphicTypeIdFieldFun.apply(changeMtd);
+ String polymorphicInstanceId = polymorphicInstanceId(schemaField.getDeclaringClass());
+
+ // if (!"first".equals(tmpVar)) throw Ex;
+ // else change_value;
+ changeMtd.getBody().append(
+ new IfStatement()
+ .condition(not(constantString(polymorphicInstanceId).invoke(STRING_EQUALS_MTD, getPolymorphicTypeIdFieldValue)))
+ .ifTrue(throwException(ConfigurationWrongPolymorphicTypeIdException.class, getPolymorphicTypeIdFieldValue))
+ .ifFalse(bytecodeBlock)
+ );
+ } else {
+ changeMtd.getBody().append(bytecodeBlock);
+ }
+
+ return changeMtd;
+ }
+
+ /**
+ * Implements changer bridge method from {@code CHANGE} interface.
+ *
+ * @param classDef Node class definition.
+ * @param changeClassName Class name for the CHANGE class.
+ * @param changeMtd Definition of change method.
+ */
+ private static void addNodeChangeBridgeMethod(
+ ClassDefinition classDef,
+ String changeClassName,
+ MethodDefinition changeMtd
+ ) {
+ MethodDefinition bridgeMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC, SYNTHETIC, BRIDGE),
+ changeMtd.getName(),
+ typeFromJavaClassName(changeClassName),
+ changeMtd.getParameters()
+ );
+
+ Variable changeVar = bridgeMtd.getScope().getVariable("change");
+
+ // this.change*(change);
+ BytecodeExpression invokeChangeMtd = bridgeMtd.getThis().invoke(changeMtd, List.of(changeVar));
+
+ // return this.change*(change);
+ bridgeMtd.getBody().append(invokeChangeMtd).retObject();
+ }
+
+ /**
+ * Implements {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} method.
+ *
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addNodeTraverseChildrenMethod(
+ Map<String, FieldDefinition> fieldDefs,
+ Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
+ @Nullable FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition traverseChildrenMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "traverseChildren",
+ type(void.class),
+ arg("visitor", type(ConfigurationVisitor.class)),
+ arg("includeInternal", type(boolean.class))
+ ).addException(NoSuchElementException.class);
+
+ BytecodeBlock mtdBody = traverseChildrenMtd.getBody();
+
+ // invokeVisit for public (common in case polymorphic config) fields.
+ for (Field schemaField : schemaFields) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ mtdBody.append(
+ invokeVisit(traverseChildrenMtd, schemaField, fieldDefs.get(schemaField.getName())).pop()
+ );
+ }
+
+ if (!internalFields.isEmpty()) {
+ BytecodeBlock includeInternalBlock = new BytecodeBlock();
+
+ for (Field internalField : internalFields) {
+ includeInternalBlock.append(
+ invokeVisit(traverseChildrenMtd, internalField, fieldDefs.get(internalField.getName())).pop()
+ );
+ }
+
+ // if (includeInternal) invokeVisit for internal fields.
+ mtdBody.append(
+ new IfStatement()
+ .condition(traverseChildrenMtd.getScope().getVariable("includeInternal"))
+ .ifTrue(includeInternalBlock)
+ );
+ } else if (!polymorphicFieldsByExtension.isEmpty()) {
+ assert polymorphicTypeIdFieldDef != null : schemaClass.getName();
+ assert schemaFields.stream().anyMatch(ConfigurationUtil::isPolymorphicId) :
+ "Missing field with @PolymorphicId in " + schemaClass.getName();
+
+ // Create switch by polymorphicTypeIdField.
+ StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(traverseChildrenMtd, polymorphicTypeIdFieldDef);
+
+ for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
+ BytecodeBlock codeBlock = new BytecodeBlock();
+
+ for (Field polymorphicField : e.getValue()) {
+ String fieldName = fieldName(polymorphicField);
+
+ // invokeVisit for specific polymorphic config fields.
+ codeBlock.append(invokeVisit(traverseChildrenMtd, polymorphicField, fieldDefs.get(fieldName)).pop());
+ }
+
+ switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), codeBlock);
+ }
+
+ // if (polymorphicTypeIdField != null) switch_by_polymorphicTypeIdField
+ mtdBody.append(
+ new IfStatement()
+ .condition(isNotNull(getThisFieldCode(traverseChildrenMtd, polymorphicTypeIdFieldDef)))
+ .ifTrue(switchBuilderTypeId.build())
+ );
+ }
+
+ mtdBody.ret();
+ }
+
+ /**
+ * Implements {@link InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} method.
+ *
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addNodeTraverseChildMethod(
+ Map<String, FieldDefinition> fieldDefs,
+ Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
+ @Nullable FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition traverseChildMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "traverseChild",
+ type(Object.class),
+ arg("key", type(String.class)),
+ arg("visitor", type(ConfigurationVisitor.class)),
+ arg("includeInternal", type(boolean.class))
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = traverseChildMtd.getScope().getVariable("key");
+
+ // Create switch for public (common in case polymorphic config) fields only.
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(traverseChildMtd.getScope()).expression(keyVar);
+
+ for (Field schemaField : schemaFields) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ String fieldName = fieldName(schemaField);
+
+ switchBuilder.addCase(
+ fieldName,
+ invokeVisit(traverseChildMtd, schemaField, fieldDefs.get(fieldName)).retObject()
+ );
+ }
+
+ if (!internalFields.isEmpty()) {
+ // Create switch for public + internal fields.
+ StringSwitchBuilder switchBuilderAllFields = new StringSwitchBuilder(traverseChildMtd.getScope())
+ .expression(keyVar)
+ .defaultCase(throwException(NoSuchElementException.class, keyVar));
+
+ for (Field schemaField : concat(schemaFields, internalFields)) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ String fieldName = fieldName(schemaField);
+
+ switchBuilderAllFields.addCase(
+ fieldName,
+ invokeVisit(traverseChildMtd, schemaField, fieldDefs.get(fieldName)).retObject()
+ );
+ }
+
+ // if (includeInternal) switch_by_all_fields
+ // else switch_only_public_fields
+ traverseChildMtd.getBody().append(
+ new IfStatement()
+ .condition(traverseChildMtd.getScope().getVariable("includeInternal"))
+ .ifTrue(switchBuilderAllFields.build())
+ .ifFalse(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
+ );
+ } else if (!polymorphicFieldsByExtension.isEmpty()) {
+ assert polymorphicTypeIdFieldDef != null : innerNodeClassDef.getName();
+
+ // Create switch by polymorphicTypeIdField.
+ StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(traverseChildMtd, polymorphicTypeIdFieldDef);
+
+ for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
+ // Create switch for specific polymorphic instance.
+ StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(traverseChildMtd.getScope())
+ .expression(keyVar)
+ .defaultCase(throwException(NoSuchElementException.class, keyVar));
+
+ for (Field polymorphicField : e.getValue()) {
+ String fieldName = fieldName(polymorphicField);
+
+ switchBuilderPolymorphicExtension.addCase(
+ polymorphicField.getName(),
+ invokeVisit(traverseChildMtd, polymorphicField, fieldDefs.get(fieldName)).retObject()
+ );
+ }
+
+ switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), switchBuilderPolymorphicExtension.build());
+ }
+
+ // switch_by_common_fields
+ // switch_by_polymorphicTypeIdField
+ // switch_by_polymorphic_0_fields
+ // switch_by_polymorphic_1_fields
+ // ...
+ traverseChildMtd.getBody()
+ .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
+ .append(switchBuilderTypeId.build());
+ } else {
+ traverseChildMtd.getBody()
+ .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build());
+ }
+ }
+
+ /**
+ * Creates bytecode block that invokes one of {@link ConfigurationVisitor}'s methods.
+ *
+ * @param mtd Method definition, either {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)} or {@link
+ * InnerNode#traverseChild(String, ConfigurationVisitor, boolean)} defined in {@code *Node} class.
+ * @param schemaField Configuration Schema field to visit.
+ * @param fieldDef Field definition from current class.
+ * @return Bytecode block that invokes "visit*" method.
+ */
+ private static BytecodeBlock invokeVisit(MethodDefinition mtd, Field schemaField, FieldDefinition fieldDef) {
+ Method visitMethod;
+
+ if (isValue(schemaField) || isPolymorphicId(schemaField)) {
+ visitMethod = VISIT_LEAF;
+ } else if (isConfigValue(schemaField)) {
+ visitMethod = VISIT_INNER;
+ } else {
+ visitMethod = VISIT_NAMED;
+ }
+
+ return new BytecodeBlock().append(mtd.getScope().getVariable("visitor").invoke(
+ visitMethod,
+ constantString(schemaField.getName()),
+ mtd.getThis().getField(fieldDef)
+ ));
+ }
+
+ /**
+ * Implements {@link ConstructableTreeNode#construct(String, ConfigurationSource, boolean)} method.
+ *
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ * @param changePolymorphicTypeIdMtd Method for changing the type of polymorphic configuration.
+ */
+ private void addNodeConstructMethod(
+ Map<String, FieldDefinition> fieldDefs,
+ Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
+ @Nullable FieldDefinition polymorphicTypeIdFieldDef,
+ @Nullable MethodDefinition changePolymorphicTypeIdMtd
+ ) {
+ MethodDefinition constructMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONSTRUCT_MTD_NAME,
+ type(void.class),
+ arg("key", type(String.class)),
+ arg("src", type(ConfigurationSource.class)),
+ arg("includeInternal", type(boolean.class))
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = constructMtd.getScope().getVariable("key");
+ Variable srcVar = constructMtd.getScope().getVariable("src");
+
+ // Create switch for public (common in case polymorphic config) fields only.
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructMtd.getScope()).expression(keyVar);
+
+ for (Field schemaField : schemaFields) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ String fieldName = fieldName(schemaField);
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ if (isPolymorphicId(schemaField)) {
+ // src == null ? null : src.unwrap(FieldType.class);
+ BytecodeExpression getTypeIdFromSrcVar = inlineIf(
+ isNull(srcVar),
+ constantNull(fieldDef.getType()),
+ srcVar.invoke(UNWRAP, constantClass(fieldDef.getType())).cast(fieldDef.getType())
+ );
+
+ // this.changePolymorphicTypeId(src == null ? null : src.unwrap(FieldType.class));
+ switchBuilder.addCase(
+ fieldName,
+ new BytecodeBlock()
+ .append(constructMtd.getThis())
+ .append(getTypeIdFromSrcVar)
+ .invokeVirtual(changePolymorphicTypeIdMtd)
+ .ret()
+ );
+ } else {
+ switchBuilder.addCase(
+ fieldName,
+ treatSourceForConstruct(constructMtd, schemaField, fieldDef).ret()
+ );
+ }
+ }
+
+ if (!internalFields.isEmpty()) {
+ // Create switch for public + internal fields.
+ StringSwitchBuilder switchBuilderAllFields = new StringSwitchBuilder(constructMtd.getScope())
+ .expression(keyVar)
+ .defaultCase(throwException(NoSuchElementException.class, keyVar));
+
+ for (Field schemaField : concat(schemaFields, internalFields)) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ String fieldName = fieldName(schemaField);
+
+ switchBuilderAllFields.addCase(
+ fieldName,
+ treatSourceForConstruct(constructMtd, schemaField, fieldDefs.get(fieldName)).ret()
+ );
+ }
+
+ // if (includeInternal) switch_by_all_fields
+ // else switch_only_public_fields
+ constructMtd.getBody().append(
+ new IfStatement().condition(constructMtd.getScope().getVariable("includeInternal"))
+ .ifTrue(switchBuilderAllFields.build())
+ .ifFalse(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
+ ).ret();
+ } else if (!polymorphicFieldsByExtension.isEmpty()) {
+ assert polymorphicTypeIdFieldDef != null : innerNodeClassDef.getName();
+
+ // Create switch by polymorphicTypeIdField.
+ StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(constructMtd, polymorphicTypeIdFieldDef);
+
+ for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
+ // Create switch for specific polymorphic instance.
+ StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(constructMtd.getScope())
+ .expression(keyVar)
+ .defaultCase(throwException(NoSuchElementException.class, keyVar));
+
+ for (Field polymorphicField : e.getValue()) {
+ String fieldName = fieldName(polymorphicField);
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ switchBuilderPolymorphicExtension.addCase(
+ polymorphicField.getName(),
+ treatSourceForConstruct(constructMtd, polymorphicField, fieldDef).ret()
+ );
+ }
+
+ switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), switchBuilderPolymorphicExtension.build());
+ }
+
+ // switch_by_common_fields
+ // switch_by_polymorphicTypeIdField
+ // switch_by_polymorphic_0_fields
+ // switch_by_polymorphic_1_fields
+ // ...
+ constructMtd.getBody()
+ .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
+ .append(switchBuilderTypeId.build())
+ .ret();
+ } else {
+ constructMtd.getBody()
+ .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
+ .ret();
+ }
+ }
+
+ /**
+ * Creates bytecode block that invokes of construct methods for {@link InnerNode#construct(String, ConfigurationSource, boolean)}.
+ *
+ * @param constructMtd Method definition {@link InnerNode#construct(String, ConfigurationSource, boolean)} defined in {@code *Node}
+ * class.
+ * @param schemaField Schema field.
+ * @param schemaFieldDef Schema field definition.
+ * @return Bytecode block that invokes of construct method for field.
+ */
+ private BytecodeBlock treatSourceForConstruct(
+ MethodDefinition constructMtd,
+ Field schemaField,
+ FieldDefinition schemaFieldDef
+ ) {
+ BytecodeBlock codeBlock = new BytecodeBlock();
+
+ Variable thisVar = constructMtd.getThis();
+ Variable srcVar = constructMtd.getScope().getVariable("src");
+
+ if (isValue(schemaField)) {
+ // this.field = src == null ? null : src.unwrap(FieldType.class);
+ codeBlock.append(thisVar.setField(schemaFieldDef, inlineIf(
+ isNull(srcVar),
+ constantNull(schemaFieldDef.getType()),
+ srcVar.invoke(UNWRAP, constantClass(schemaFieldDef.getType())).cast(schemaFieldDef.getType())
+ )));
+ } else if (isConfigValue(schemaField)) {
+ BytecodeNode setField;
+
+ ParameterizedType fieldDefType = schemaFieldDef.getType();
+
+ if (isPolymorphicConfig(schemaField.getType())) {
+ Field polymorphicIdField = polymorphicIdField(schemaField.getType());
+
+ assert polymorphicIdField != null : schemaField.getType().getName();
+
+ // this.field;
+ BytecodeExpression thisField = getThisFieldCode(constructMtd, schemaFieldDef);
+
+ // String tmpStr;
+ Variable tmpStrVar = constructMtd.getScope().createTempVariable(String.class);
+
+ // this.field = (FieldType) this.field.copy();
+ // if(tmpStr != null) this.field.changeTypeId(tmpStr);
+ BytecodeBlock copyWithChange = new BytecodeBlock()
+ .append(setThisFieldCode(constructMtd, thisField.invoke(COPY).cast(fieldDefType), schemaFieldDef))
+ .append(new IfStatement()
+ .condition(isNotNull(tmpStrVar))
+ .ifTrue(thisField.invoke(changeMethodName(polymorphicIdField.getName()), void.class, tmpStrVar))
+ );
+
+ // this.field = new FieldType();
+ // if(tmpStr != null) this.field.changeTypeId(tmpStr);
+ // else {
+ // this.field.constructDefault("typeId");
+ // if(this.field.typeId == null) throw new IllegalStateException();
+ // }
+ BytecodeBlock newInstanceWithChange = new BytecodeBlock()
+ .append(setThisFieldCode(constructMtd, newInstance(fieldDefType), schemaFieldDef))
+ .append(new IfStatement()
+ .condition(isNotNull(tmpStrVar))
+ .ifTrue(thisField.invoke(changeMethodName(polymorphicIdField.getName()), void.class, tmpStrVar))
+ .ifFalse(new BytecodeBlock()
+ .append(thisField.invoke(CONSTRUCT_DEFAULT_MTD, constantString(polymorphicIdField.getName())))
+ .append(new IfStatement()
+ .condition(isNull(thisField.getField(polymorphicIdField.getName(), String.class)))
+ .ifTrue(throwException(
+ IllegalStateException.class,
+ constantString(polymorphicTypeNotDefinedErrorMessage(
+ polymorphicIdField))
+ ))
+ )
+ )
+ );
+
+ // tmpStr = src.polymorphicTypeId("typeId");
+ // if(this.field == null)
+ setField = new BytecodeBlock()
+ .append(tmpStrVar.set(srcVar.invoke(POLYMORPHIC_TYPE_ID_MTD, constantString(polymorphicIdField.getName()))))
+ .append(new IfStatement()
+ .condition(isNull(thisField))
+ .ifTrue(newInstanceWithChange)
+ .ifFalse(copyWithChange)
+ );
+ } else {
+ // newValue = this.field == null ? new ValueNode() : field.copy());
+ BytecodeExpression newValue = cgen.newOrCopyNodeField(schemaField, getThisFieldCode(constructMtd, schemaFieldDef));
+
+ // this.field = newValue;
+ setField = setThisFieldCode(constructMtd, newValue, schemaFieldDef);
+ }
+
+ if (containsNameAnnotation(schemaField)) {
+ setField = new BytecodeBlock()
+ .append(setField)
+ .append(getThisFieldCode(constructMtd, schemaFieldDef).invoke(
+ SET_INJECTED_NAME_FIELD_VALUE_MTD,
+ constantString(schemaField.getAnnotation(Name.class).value())
+ ));
+ }
+
+ codeBlock.append(
+ new IfStatement()
+ .condition(isNull(srcVar))
+ .ifTrue(setThisFieldCode(constructMtd, constantNull(fieldDefType), schemaFieldDef))
+ .ifFalse(new BytecodeBlock()
+ .append(setField)
+ .append(srcVar.invoke(DESCEND, thisVar.getField(schemaFieldDef)))
+ )
+ );
+ } else {
+ // this.field = src == null ? new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName")
+ // : src.descend(field = field.copy()));
+ codeBlock.append(new IfStatement()
+ .condition(isNull(srcVar))
+ .ifTrue(setThisFieldCode(constructMtd, cgen.newNamedListNode(schemaField), schemaFieldDef))
+ .ifFalse(new BytecodeBlock()
+ .append(setThisFieldCode(
+ constructMtd,
+ thisVar.getField(schemaFieldDef).invoke(COPY).cast(schemaFieldDef.getType()),
+ schemaFieldDef
+ )).append(srcVar.invoke(DESCEND, thisVar.getField(schemaFieldDef)))
+ )
+ );
+ }
+
+ return codeBlock;
+ }
+
+ /**
+ * Implements {@link InnerNode#constructDefault(String)} method.
+ *
+ * @param specFields Field definitions for the schema and its extensions: {@code _spec#}.
+ * @param fieldDefs Definitions for all fields in {@code schemaFields}.
+ * @param polymorphicFieldsByExtension Fields of polymorphic configuration instances grouped by them.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addNodeConstructDefaultMethod(
+ Map<Class<?>, FieldDefinition> specFields,
+ Map<String, FieldDefinition> fieldDefs,
+ Map<Class<?>, List<Field>> polymorphicFieldsByExtension,
+ @Nullable FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition constructDfltMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "constructDefault",
+ type(void.class),
+ arg("key", String.class)
+ ).addException(NoSuchElementException.class);
+
+ Variable keyVar = constructDfltMtd.getScope().getVariable("key");
+
+ // Create switch for public (common in case polymorphic config) + internal fields.
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(constructDfltMtd.getScope()).expression(keyVar);
+
+ for (Field schemaField : concat(schemaFields, internalFields)) {
+ if (isInjectedName(schemaField)) {
+ continue;
+ }
+
+ if (isValue(schemaField) || isPolymorphicId(schemaField)) {
+ String fieldName = schemaField.getName();
+
+ if (isValue(schemaField) && !hasDefault(schemaField)
+ || isPolymorphicId(schemaField) && !schemaField.getAnnotation(PolymorphicId.class).hasDefault()) {
+ // return;
+ switchBuilder.addCase(fieldName, new BytecodeBlock().ret());
+ } else {
+ FieldDefinition fieldDef = fieldDefs.get(fieldName);
+
+ Class<?> fieldType = schemaField.getDeclaringClass();
+
+ FieldDefinition specFieldDef = fieldType.isAnnotationPresent(AbstractConfiguration.class)
+ ? specFields.get(schemaClass)
+ : specFields.get(fieldType);
+
+ // this.field = spec_#.field;
+ switchBuilder.addCase(
+ fieldName,
+ addNodeConstructDefault(constructDfltMtd, schemaField, fieldDef, specFieldDef).ret()
+ );
+ }
+ }
+ }
+
+ if (!polymorphicFieldsByExtension.isEmpty()) {
+ // Create switch by polymorphicTypeIdField.
+ StringSwitchBuilder switchBuilderTypeId = typeIdSwitchBuilder(constructDfltMtd, polymorphicTypeIdFieldDef);
+
+ for (Map.Entry<Class<?>, List<Field>> e : polymorphicFieldsByExtension.entrySet()) {
+ // Create switch for specific polymorphic instance.
+ StringSwitchBuilder switchBuilderPolymorphicExtension = new StringSwitchBuilder(constructDfltMtd.getScope())
+ .expression(keyVar)
+ .defaultCase(throwException(NoSuchElementException.class, keyVar));
+
+ for (Field polymorphicField : e.getValue()) {
+ if (isValue(polymorphicField)) {
+ if (!hasDefault(polymorphicField)) {
+ // return;
+ switchBuilderPolymorphicExtension.addCase(polymorphicField.getName(), new BytecodeBlock().ret());
+ } else {
+ FieldDefinition fieldDef = fieldDefs.get(fieldName(polymorphicField));
+ FieldDefinition specFieldDef = specFields.get(polymorphicField.getDeclaringClass());
+
+ // this.field = spec_#.field;
+ switchBuilderPolymorphicExtension.addCase(
+ polymorphicField.getName(),
+ addNodeConstructDefault(constructDfltMtd, polymorphicField, fieldDef, specFieldDef).ret()
+ );
+ }
+ }
+ }
+
+ switchBuilderTypeId.addCase(
+ polymorphicInstanceId(e.getKey()),
+ switchBuilderPolymorphicExtension.build()
+ );
+ }
+
+ // switch_by_common_fields
+ // switch_by_polymorphicTypeIdField
+ // switch_by_polymorphic_0_fields
+ // switch_by_polymorphic_1_fields
+ // ...
+ constructDfltMtd.getBody()
+ .append(switchBuilder.defaultCase(new BytecodeBlock()).build())
+ .append(switchBuilderTypeId.build())
+ .ret();
+ } else {
+ constructDfltMtd.getBody()
+ .append(switchBuilder.defaultCase(throwException(NoSuchElementException.class, keyVar)).build())
+ .ret();
+ }
+ }
+
+ /**
+ * Creates a bytecode block of code that sets the default value for a field from the schema for {@link
+ * InnerNode#constructDefault(String)}.
+ *
+ * @param constructDfltMtd Method definition {@link InnerNode#constructDefault(String)} defined in {@code *Node} class.
+ * @param schemaField Schema field.
+ * @param schemaFieldDef Schema field definition.
+ * @param specFieldDef Definition of the schema field.: {@code _spec#}.
+ * @return Bytecode block that sets the default value for a field from the schema.
+ */
+ private static BytecodeBlock addNodeConstructDefault(
+ MethodDefinition constructDfltMtd,
+ Field schemaField,
+ FieldDefinition schemaFieldDef,
+ FieldDefinition specFieldDef
+ ) {
+ Variable thisVar = constructDfltMtd.getThis();
+
+ // defaultValue = _spec#.field;
+ BytecodeExpression defaultValue = thisVar.getField(specFieldDef).getField(schemaField);
+
+ Class<?> schemaFieldType = schemaField.getType();
+
+ // defaultValue = Box.valueOf(defaultValue); // Boxing.
+ if (schemaFieldType.isPrimitive()) {
+ defaultValue = invokeStatic(
+ schemaFieldDef.getType(),
+ "valueOf",
+ schemaFieldDef.getType(),
+ singleton(defaultValue)
+ );
+ }
+
+ // defaultValue = defaultValue.clone();
+ if (schemaFieldType.isArray()) {
+ defaultValue = defaultValue.invoke("clone", Object.class).cast(schemaFieldType);
+ }
+
+ // this.field = defaultValue;
+ return new BytecodeBlock().append(thisVar.setField(schemaFieldDef, defaultValue));
+ }
+
+ /**
+ * Adds method overrides {@link InnerNode#getInjectedNameFieldValue} and {@link InnerNode#setInjectedNameFieldValue}.
+ *
+ * @param injectedNameFieldDef Field definition with {@link InjectedName}.
+ */
+ private void addInjectedNameFieldMethods(FieldDefinition injectedNameFieldDef) {
+ MethodDefinition getInjectedNameFieldValueMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "getInjectedNameFieldValue",
+ type(String.class)
+ );
+
+ getInjectedNameFieldValueMtd.getBody()
+ .append(getThisFieldCode(getInjectedNameFieldValueMtd, injectedNameFieldDef))
+ .retObject();
+
+ MethodDefinition setInjectedNameFieldValueMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "setInjectedNameFieldValue",
+ type(void.class),
+ arg("value", String.class)
+ );
+
+ Variable valueVar = setInjectedNameFieldValueMtd.getScope().getVariable("value");
+
+ setInjectedNameFieldValueMtd.getBody()
+ .append(invokeStatic(REQUIRE_NON_NULL, valueVar, constantString("value")))
+ .append(setThisFieldCode(
+ setInjectedNameFieldValueMtd,
+ valueVar,
+ injectedNameFieldDef
+ )).ret();
+ }
+
+ /**
+ * Adds an override for the {@link InnerNode#isPolymorphic} method that returns {@code true}.
+ */
+ private void addIsPolymorphicMethod() {
+ MethodDefinition mtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ IS_POLYMORPHIC_MTD.getName(),
+ type(boolean.class)
+ );
+
+ mtd.getBody()
+ .push(true)
+ .retBoolean();
+ }
+
+ /**
+ * Adds an override for the {@link InnerNode#internalSchemaTypes} method that returns field {@code internalSchemaTypesFieldDef}.
+ *
+ * @param internalSchemaTypesFieldDef Final field of {@link InnerNode}, which stores all schemes for internal configuration extensions.
+ */
+ private void addInternalSchemaTypesMethod(
+ FieldDefinition internalSchemaTypesFieldDef
+ ) {
+ MethodDefinition mtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ INTERNAL_SCHEMA_TYPES_MTD.getName(),
+ type(Class[].class)
+ );
+
+ mtd.getBody()
+ .append(getThisFieldCode(mtd, internalSchemaTypesFieldDef))
+ .retObject();
+ }
+
+ /**
+ * Adds an override for the {@link InnerNode#extendsAbstractConfiguration()} method that returns {@code true}.
+ */
+ private void addIsExtendAbstractConfigurationMethod() {
+ MethodDefinition mtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "extendsAbstractConfiguration",
+ type(boolean.class)
+ );
+
+ mtd.getBody()
+ .push(true)
+ .retBoolean();
+ }
+
+ /**
+ * Create a {@code *Node} for the polymorphic configuration instance schema.
+ *
+ * @param polymorphicExtension Polymorphic configuration instance schema (child).
+ * @param polymorphicFields Schema fields of a polymorphic configuration instance {@code polymorphicExtension}.
+ */
+ private ClassDefinition createPolymorphicExtensionNodeClass(
+ Class<?> polymorphicExtension,
+ Collection<Field> polymorphicFields
+ ) {
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+ SchemaClassesInfo polymorphicExtensionClassInfo = cgen.schemaInfo(polymorphicExtension);
+
+ // Node class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ EnumSet.of(PUBLIC, FINAL),
+ internalName(polymorphicExtensionClassInfo.nodeClassName),
+ type(Object.class),
+ ArrayUtils.concat(nodeClassInterfaces(polymorphicExtension, Set.of()), type(ConstructableTreeNode.class))
+ );
+
+ // private final ParentNode this$0;
+ FieldDefinition parentInnerNodeFieldDef = classDef.declareField(
+ EnumSet.of(PRIVATE, FINAL),
+ "this$0",
+ typeFromJavaClassName(schemaClassInfo.nodeClassName)
+ );
+
+ // Constructor.
+ MethodDefinition constructorMtd = classDef.declareConstructor(
+ EnumSet.of(PUBLIC),
+ arg("delegate", typeFromJavaClassName(schemaClassInfo.nodeClassName))
+ );
+
+ Variable delegateVar = constructorMtd.getScope().getVariable("delegate");
+
+ // Constructor body.
+ constructorMtd.getBody()
+ .append(constructorMtd.getThis())
+ .invokeConstructor(Object.class)
+ .append(constructorMtd.getThis().setField(
+ parentInnerNodeFieldDef,
+ delegateVar
+ ))
+ .ret();
+
+ Map<String, FieldDefinition> fieldDefs = innerNodeClassDef.getFields().stream()
+ .collect(toMap(FieldDefinition::getName, identity()));
+
+ // Creates method to get the internal id. Almost the same as regular view, but with method invocation instead of field access.
+ if (internalIdField != null) {
+ addNodeViewMethod(
+ classDef,
+ internalIdField,
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef).invoke(INTERNAL_ID),
+ null
+ );
+ }
+
+ // Creates view and change methods for parent schema.
+ for (Field schemaField : schemaFields) {
+ FieldDefinition schemaFieldDef = fieldDefs.get(fieldName(schemaField));
+
+ addNodeViewMethod(
+ classDef,
+ schemaField,
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, schemaFieldDef),
+ null
+ );
+
+ // Read only.
+ if (isPolymorphicId(schemaField) || isInjectedName(schemaField)) {
+ continue;
+ }
+
+ MethodDefinition changeMtd0 = addNodeChangeMethod(
+ classDef,
+ schemaField,
+ changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, schemaFieldDef),
+ (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, parentInnerNodeFieldDef, schemaFieldDef),
+ null
+ );
+
+ addNodeChangeBridgeMethod(classDef, schemaClassInfo.changeClassName, changeMtd0);
+ }
+
+ FieldDefinition polymorphicTypeIdFieldDef = fieldDefs.get(polymorphicIdField(schemaClass).getName());
+
+ // Creates view and change methods for specific polymorphic instance schema.
+ for (Field polymorphicField : polymorphicFields) {
+ FieldDefinition polymorphicFieldDef = fieldDefs.get(fieldName(polymorphicField));
+
+ addNodeViewMethod(
+ classDef,
+ polymorphicField,
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, polymorphicFieldDef),
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef, polymorphicTypeIdFieldDef)
+ );
+
+ MethodDefinition changeMtd0 = addNodeChangeMethod(
+ classDef,
+ polymorphicField,
+ changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, polymorphicFieldDef),
+ (changeMtd, newValue) -> setThisFieldCode(changeMtd, newValue, parentInnerNodeFieldDef, polymorphicFieldDef),
+ changeMtd -> getThisFieldCode(changeMtd, parentInnerNodeFieldDef, polymorphicTypeIdFieldDef)
+ );
+
+ addNodeChangeBridgeMethod(classDef, polymorphicExtensionClassInfo.changeClassName, changeMtd0);
+ }
+
+ ParameterizedType returnType = typeFromJavaClassName(schemaClassInfo.changeClassName);
+
+ // Creates Node#convert(Class<T> changeClass).
+ MethodDefinition convertByChangeClassMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONVERT_MTD_NAME,
+ returnType,
+ arg("changeClass", Class.class)
+ );
+
+ // return this.this$0.convert(changeClass);
+ convertByChangeClassMtd.getBody()
+ .append(getThisFieldCode(convertByChangeClassMtd, parentInnerNodeFieldDef))
+ .append(convertByChangeClassMtd.getScope().getVariable("changeClass"))
+ .invokeVirtual(innerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(Class.class))
+ .retObject();
+
+ // Creates Node#convert(String polymorphicId).
+ MethodDefinition convertByPolymorphicTypeIdMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONVERT_MTD_NAME,
+ returnType,
+ arg("polymorphicTypeId", String.class)
+ );
+
+ // return this.this$0.convert(polymorphicTypeId);
+ convertByPolymorphicTypeIdMtd.getBody()
+ .append(getThisFieldCode(convertByPolymorphicTypeIdMtd, parentInnerNodeFieldDef))
+ .append(convertByPolymorphicTypeIdMtd.getScope().getVariable("polymorphicTypeId"))
+ .invokeVirtual(innerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(String.class))
+ .retObject();
+
+ // Creates ConstructableTreeNode#construct.
+ MethodDefinition constructMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONSTRUCT_MTD_NAME,
+ type(void.class),
+ arg("key", type(String.class)),
+ arg("src", type(ConfigurationSource.class)),
+ arg("includeInternal", type(boolean.class))
+ ).addException(NoSuchElementException.class);
+
+ // return this.this$0.construct(key, src, includeInternal);
+ constructMtd.getBody()
+ .append(getThisFieldCode(constructMtd, parentInnerNodeFieldDef))
+ .append(constructMtd.getScope().getVariable("key"))
+ .append(constructMtd.getScope().getVariable("src"))
+ .append(constructMtd.getScope().getVariable("includeInternal"))
+ .invokeVirtual(
+ innerNodeClassDef.getType(),
+ CONSTRUCT_MTD_NAME,
+ type(void.class),
+ type(String.class), type(ConfigurationSource.class), type(boolean.class)
+ )
+ .ret();
+
+ // Creates ConstructableTreeNode#copy.
+ MethodDefinition copyMtd = classDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ "copy",
+ type(ConstructableTreeNode.class)
+ );
+
+ // return this.this$0.copy();
+ copyMtd.getBody()
+ .append(getThisFieldCode(copyMtd, parentInnerNodeFieldDef))
+ .invokeVirtual(innerNodeClassDef.getType(), "copy", type(ConstructableTreeNode.class))
+ .retObject();
+
+ return classDef;
+ }
+
+ /**
+ * Adds a {@link InnerNode#specificNode} override for the polymorphic configuration case.
+ *
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ */
+ private void addNodeSpecificNodeMethod(FieldDefinition polymorphicTypeIdFieldDef) {
+ MethodDefinition specificNodeMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ SPECIFIC_NODE_MTD.getName(),
+ type(Object.class)
+ );
+
+ StringSwitchBuilder switchBuilder = typeIdSwitchBuilder(specificNodeMtd, polymorphicTypeIdFieldDef);
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ SchemaClassesInfo polymorphicExtensionClassInfo = cgen.schemaInfo(polymorphicExtension);
+
+ switchBuilder.addCase(
+ polymorphicInstanceId(polymorphicExtension),
+ newInstance(
+ typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+ specificNodeMtd.getThis()
+ ).ret()
+ );
+ }
+
+ specificNodeMtd.getBody().append(switchBuilder.build());
+ }
+
+ /**
+ * Adds a {@code *Node#convert(Class changeClass)} and {@code *Node#convert(String polymorphicTypeId)} for the polymorphic configuration
+ * case.
+ *
+ * @param changePolymorphicTypeIdMtd Method for changing the type of polymorphic configuration.
+ */
+ private void addNodeConvertMethods(MethodDefinition changePolymorphicTypeIdMtd) {
+ SchemaClassesInfo schemaClassInfo = cgen.schemaInfo(schemaClass);
+
+ MethodDefinition convertByChangeClassMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONVERT_MTD_NAME,
+ typeFromJavaClassName(schemaClassInfo.changeClassName),
+ arg("changeClass", Class.class)
+ );
+
+ MethodDefinition convertByPolymorphicTypeIdMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ CONVERT_MTD_NAME,
+ typeFromJavaClassName(schemaClassInfo.changeClassName),
+ arg("polymorphicTypeId", String.class)
+ );
+
+ // changeClass.getName();
+ BytecodeExpression changeClassName = convertByChangeClassMtd.getScope()
+ .getVariable("changeClass")
+ .invoke(CLASS_GET_NAME_MTD);
+
+ StringSwitchBuilder switchByChangeClassBuilder = new StringSwitchBuilder(convertByChangeClassMtd.getScope())
+ .expression(changeClassName)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, changeClassName));
+
+ Variable polymorphicTypeId = convertByPolymorphicTypeIdMtd.getScope()
+ .getVariable("polymorphicTypeId");
+
+ StringSwitchBuilder switchByPolymorphicTypeIdBuilder = new StringSwitchBuilder(convertByPolymorphicTypeIdMtd.getScope())
+ .expression(polymorphicTypeId)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, polymorphicTypeId));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ SchemaClassesInfo polymorphicExtensionClassInfo = cgen.schemaInfo(polymorphicExtension);
+
+ String polymorphicInstanceId = polymorphicInstanceId(polymorphicExtension);
+
+ // case "HashIndexChange":
+ // this.changePolymorphicTypeId("hashIndex");
+ // return new HashIndexNode(this);
+ switchByChangeClassBuilder.addCase(
+ polymorphicExtensionClassInfo.changeClassName,
+ new BytecodeBlock()
+ .append(constantString(polymorphicInstanceId))
+ .invokeVirtual(changePolymorphicTypeIdMtd)
+ .append(newInstance(
+ typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+ convertByChangeClassMtd.getThis()
+ ))
+ .retObject()
+ );
+
+ // case "hashIndex":
+ // this.changePolymorphicTypeId("hashIndex");
+ // return new HashIndexNode(this);
+ switchByPolymorphicTypeIdBuilder.addCase(
+ polymorphicInstanceId,
+ new BytecodeBlock()
+ .append(constantString(polymorphicInstanceId))
+ .invokeVirtual(changePolymorphicTypeIdMtd)
+ .append(newInstance(
+ typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+ convertByPolymorphicTypeIdMtd.getThis()
+ ))
+ .retObject()
+ );
+ }
+
+ convertByChangeClassMtd.getBody()
+ .append(convertByChangeClassMtd.getThis())
+ .append(switchByChangeClassBuilder.build())
+ .ret();
+
+ convertByPolymorphicTypeIdMtd.getBody()
+ .append(convertByPolymorphicTypeIdMtd.getThis())
+ .append(switchByPolymorphicTypeIdBuilder.build())
+ .ret();
+ }
+
+ /**
+ * Adds a {@code Node#changeTypeId} for the polymorphic configuration case.
+ *
+ * @param fieldDefs Definitions for all fields in {@code innerNodeClassDef}.
+ * @param polymorphicTypeIdFieldDef Identification field for the polymorphic configuration instance.
+ * @return Method definition.
+ */
+ private MethodDefinition addNodeChangePolymorphicTypeIdMethod(
+ Map<String, FieldDefinition> fieldDefs,
+ FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition changePolymorphicTypeIdMtd = innerNodeClassDef.declareMethod(
+ EnumSet.of(PUBLIC),
+ changeMethodName(polymorphicTypeIdFieldDef.getName()),
+ type(void.class),
+ arg("typeId", String.class)
+ );
+
+ Variable typeIdVar = changePolymorphicTypeIdMtd.getScope().getVariable("typeId");
+
+ StringSwitchBuilder switchBuilder = new StringSwitchBuilder(changePolymorphicTypeIdMtd.getScope())
+ .expression(typeIdVar)
+ .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, typeIdVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ // Fields that need to be cleared when changing the type of the polymorphic configuration instance.
+ Collection<Field> resetFields = polymorphicFields.stream()
+ .filter(f -> !polymorphicExtension.equals(f.getDeclaringClass()))
+ .collect(toList());
+
+ // this.typeId = typeId;
+ BytecodeBlock codeBlock = new BytecodeBlock()
+ .append(setThisFieldCode(changePolymorphicTypeIdMtd, typeIdVar, polymorphicTypeIdFieldDef));
+
+ // Reset fields.
+ for (Field resetField : resetFields) {
+ FieldDefinition fieldDef = fieldDefs.get(fieldName(resetField));
+
+ if (isValue(resetField) || isConfigValue(resetField)) {
+ // this.field = null;
+ codeBlock.append(setThisFieldCode(
+ changePolymorphicTypeIdMtd,
+ constantNull(fieldDef.getType()),
+ fieldDef
+ ));
+ } else {
+ // this.field = new NamedListNode<>(key, ValueNode::new, "polymorphicIdFieldName");
+ codeBlock.append(setThisFieldCode(changePolymorphicTypeIdMtd, cgen.newNamedListNode(resetField), fieldDef));
+ }
+ }
+
+ // ConfigurationUtil.addDefaults(this);
+ codeBlock
+ .append(changePolymorphicTypeIdMtd.getThis())
+ .invokeStatic(ADD_DEFAULTS_MTD);
+
+ switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension), codeBlock);
+ }
+
+ // if(typeId.equals(this.typeId)) return;
+ // else switch(typeId)...
+ changePolymorphicTypeIdMtd.getBody()
+ .append(typeIdVar)
+ .append(getThisFieldCode(changePolymorphicTypeIdMtd, polymorphicTypeIdFieldDef))
+ .append(
+ new IfStatement()
+ .condition(new BytecodeBlock().invokeVirtual(STRING_EQUALS_MTD))
+ .ifTrue(new BytecodeBlock().ret())
+ .ifFalse(switchBuilder.build().ret())
+ );
+
+ return changePolymorphicTypeIdMtd;
+ }
+
+ private String polymorphicTypeNotDefinedErrorMessage(Field polymorphicIdField) {
+ return "Polymorphic configuration type is not defined: "
+ + polymorphicIdField.getDeclaringClass().getName()
+ + ". See @" + PolymorphicConfig.class.getSimpleName() + " documentation.";
+ }
+}