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.";
+    }
+}