ibessonov commented on code in PR #1431:
URL: https://github.com/apache/ignite-3/pull/1431#discussion_r1048105817


##########
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationImplAsmGenerator.java:
##########
@@ -0,0 +1,872 @@
+/*
+ * 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.EnumSet.of;
+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.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+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 {
+    /** 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(
+                of(PUBLIC, FINAL),
+                internalName(schemaClassInfo.cfgImplClassName),
+                type(DynamicConfiguration.class),
+                cgen.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(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(
+                    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 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 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(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(
+                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(
+                            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(
+                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.

Review Comment:
   I wouldn't want to change the code that I moved too much :) Bu I'll do it 
here. Just wanted to remind that I didn't write this code.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to