You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2020/12/16 15:25:28 UTC

[ignite-3] branch ignite-13618-javapoet updated: WIP. Fix generator. Fix styles.

This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch ignite-13618-javapoet
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/ignite-13618-javapoet by this push:
     new 0b13416  WIP. Fix generator. Fix styles.
0b13416 is described below

commit 0b13416bb28c608cd6093c278426b00ce695693e
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Wed Dec 16 18:25:08 2020 +0300

    WIP.
    Fix generator. Fix styles.
---
 .../schema/marshaller/AbstractSerializer.java      | 104 ++++++++
 .../internal/schema/marshaller/BinaryMode.java     |  20 +-
 .../{MarshallerUtil.java => CompilerUtils.java}    | 289 ++++++++++-----------
 .../internal/schema/marshaller/MarshallerUtil.java | 272 +------------------
 .../schema/marshaller/SerializationException.java  |   2 +-
 .../internal/schema/marshaller/Serializer.java     |   2 +
 .../schema/marshaller/SerializerFactory.java       |   2 +-
 .../IdentityObjectMarshallerExprGenerator.java     |  22 +-
 .../generator/MarshallerCodeGenerator.java         |  47 ++++
 ...tor.java => ObjectMarshallerCodeGenerator.java} |  74 +++---
 .../marshaller/generator/SerializerGenerator.java  | 159 ++++++++----
 ...pr.java => TupleColumnAccessCodeGenerator.java} | 157 ++++++++---
 .../marshaller/reflection/FieldAccessor.java       | 154 ++++++++++-
 .../marshaller/reflection/JavaSerializer.java      | 148 -----------
 .../ignite/internal/util/IgniteUnsafeUtils.java    | 273 -------------------
 .../benchmarks/SerializerBenchmarkTest.java        |  10 +-
 .../schema/marshaller/JavaSerializerTest.java      |   6 +-
 17 files changed, 734 insertions(+), 1007 deletions(-)

diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java
new file mode 100644
index 0000000..86e4355
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java
@@ -0,0 +1,104 @@
+/*
+ * 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.schema.marshaller;
+
+import java.util.Objects;
+import org.apache.ignite.internal.schema.ByteBufferTuple;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.util.Pair;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Base serializer class.
+ */
+public abstract class AbstractSerializer implements Serializer {
+    /** Schema descriptor. */
+    protected final SchemaDescriptor schema;
+
+    /**
+     * Constructor.
+     * @param schema Schema descriptor.
+     */
+    protected AbstractSerializer(SchemaDescriptor schema) {
+        this.schema = schema;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] serialize(Object key, Object val) throws SerializationException {
+        final TupleAssembler assembler = createAssembler(Objects.requireNonNull(key), val);
+
+        return serialize0(assembler, key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object deserializeKey(byte[] data) throws SerializationException {
+        final Tuple tuple = new ByteBufferTuple(schema, data);
+
+        return deserializeKey0(tuple);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object deserializeValue(byte[] data) throws SerializationException {
+        final Tuple tuple = new ByteBufferTuple(schema, data);
+
+        return  deserializeValue0(tuple);
+    }
+
+    /** {@inheritDoc} */
+    public <K, V> Pair<K, V> deserialize(byte[] data) throws SerializationException {
+        final Tuple tuple = new ByteBufferTuple(schema, data);
+
+        return new Pair<>((K)deserializeKey0(tuple), (V)deserializeValue0(tuple));
+    }
+
+    /**
+     * Tuple assembler factory method.
+     */
+    protected abstract TupleAssembler createAssembler(Object key, @Nullable Object val);
+
+    /**
+     * Internal serialization method.
+     *
+     * @param asm Tuple assembler.
+     * @param keyObj Key object.
+     * @param valObj Value object.
+     * @return Serialized pair.
+     * @throws SerializationException If failed.
+     */
+    protected abstract byte[] serialize0(TupleAssembler asm, Object keyObj, Object valObj) throws SerializationException;
+
+    /**
+     * Extract key object from tuple.
+     *
+     * @param tuple Tuple.
+     * @return Deserialized key object.
+     * @throws SerializationException If failed.
+     */
+    protected abstract Object deserializeKey0(Tuple tuple) throws SerializationException;
+
+    /**
+     * Extract value object from tuple.
+     *
+     * @param tuple Tuple.
+     * @return Deserialized value object.
+     * @throws SerializationException If failed.
+     */
+    protected abstract Object deserializeValue0(Tuple tuple) throws SerializationException;
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
index db5a77f..6a0c6ab 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
@@ -41,34 +41,34 @@ public enum BinaryMode {
     /** Primitive int. */
     P_DOUBLE(NativeTypeSpec.DOUBLE),
 
-    /** */
+    /** Boxed byte. */
     BYTE(NativeTypeSpec.BYTE),
 
-    /** */
+    /** Boxed short. */
     SHORT(NativeTypeSpec.SHORT),
 
-    /** */
+    /** Boxed int. */
     INT(NativeTypeSpec.INTEGER),
 
-    /** */
+    /** Boxed long. */
     LONG(NativeTypeSpec.LONG),
 
-    /** */
+    /** Boxed float. */
     FLOAT(NativeTypeSpec.FLOAT),
 
-    /** */
+    /** Boxed double. */
     DOUBLE(NativeTypeSpec.DOUBLE),
 
-    /** */
+    /** String. */
     STRING(NativeTypeSpec.STRING),
 
-    /** */
+    /** Uuid. */
     UUID(NativeTypeSpec.UUID),
 
-    /** */
+    /** Raw byte array. */
     BYTE_ARR(NativeTypeSpec.BYTES),
 
-    /** */
+    /** BitSet.*/
     BITSET(NativeTypeSpec.BITMASK);
 
     /** Natove type spec. */
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java
similarity index 60%
copy from modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
copy to modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java
index 4f36129..0edaf68 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java
@@ -26,14 +26,16 @@ import java.io.OutputStream;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.util.BitSet;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
-import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import javax.tools.Diagnostic;
 import javax.tools.DiagnosticCollector;
 import javax.tools.FileObject;
@@ -41,112 +43,38 @@ import javax.tools.ForwardingJavaFileManager;
 import javax.tools.JavaCompiler;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
-import javax.tools.JavaFileObject.Kind;
 import javax.tools.SimpleJavaFileObject;
 import javax.tools.StandardLocation;
 import javax.tools.ToolProvider;
-import org.apache.ignite.internal.schema.NativeType;
-import org.apache.ignite.internal.schema.TupleAssembler;
-import org.apache.ignite.internal.util.ObjectFactory;
 import org.jetbrains.annotations.Nullable;
 
 /**
- * Marshaller utility class.
+ * Compiler utility class.
  */
-public final class MarshallerUtil {
+public final class CompilerUtils {
     /**
-     * Calculates size for serialized value of varlen type.
-     *
-     * @param val Field value.
-     * @param type Mapped type.
-     * @return Serialized value size.
+     * @return Creates classloader for dynamically loaded in-memory classes.
      */
-    public static int getValueSize(Object val, NativeType type) {
-        switch (type.spec()) {
-            case BYTES:
-                return ((byte[])val).length;
-
-            case STRING:
-                return TupleAssembler.utf8EncodedLength((CharSequence)val);
-
-            default:
-                throw new IllegalStateException("Unsupported test varsize type: " + type);
-        }
+    public static ClassLoader dynamicClassLoader() {
+        return AccessController.doPrivileged(new PrivilegedAction<>() {
+            @Override public ClassLoader run() {
+                return new MemoryClassLoader(new ConcurrentHashMap<>(), ClassLoader.getSystemClassLoader());
+            }
+        });
     }
 
     /**
-     * Gets binary read/write mode for given class.
+     * Compiles code and load compiled classes.
      *
-     * @param cls Type.
-     * @return Binary mode.
-     */
-    public static BinaryMode mode(Class<?> cls) {
-        assert cls != null;
-
-        // Primitives.
-        if (cls == byte.class)
-            return BinaryMode.P_BYTE;
-        else if (cls == short.class)
-            return BinaryMode.P_SHORT;
-        else if (cls == int.class)
-            return BinaryMode.P_INT;
-        else if (cls == long.class)
-            return BinaryMode.P_LONG;
-        else if (cls == float.class)
-            return BinaryMode.P_FLOAT;
-        else if (cls == double.class)
-            return BinaryMode.P_DOUBLE;
-
-            // Boxed primitives.
-        else if (cls == Byte.class)
-            return BinaryMode.BYTE;
-        else if (cls == Short.class)
-            return BinaryMode.SHORT;
-        else if (cls == Integer.class)
-            return BinaryMode.INT;
-        else if (cls == Long.class)
-            return BinaryMode.LONG;
-        else if (cls == Float.class)
-            return BinaryMode.FLOAT;
-        else if (cls == Double.class)
-            return BinaryMode.DOUBLE;
-
-            // Other types
-        else if (cls == byte[].class)
-            return BinaryMode.BYTE_ARR;
-        else if (cls == String.class)
-            return BinaryMode.STRING;
-        else if (cls == UUID.class)
-            return BinaryMode.UUID;
-        else if (cls == BitSet.class)
-            return BinaryMode.BITSET;
-
-        return null;
-    }
-
-    public static <T> ObjectFactory<T> factoryForClass(Class<T> type) {
-        if (mode(type) == null)
-            return new ObjectFactory<>(type);
-        else
-            return null;
-    }
-
-    /**
-     * Stub.
+     * @param javafile Java file representation.
+     * @return Classloader with compiled classes.
      */
-    private MarshallerUtil() {
-    }
-
     public static ClassLoader compileCode(JavaFile javafile) {
-        return compileCode(javafile, null);
-    }
-
-    public static ClassLoader compileCode(JavaFile javafile, Map<String, byte[]> classes) {
         final JavaCompiler cmp = ToolProvider.getSystemJavaCompiler();
 
         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 
-        Map<String, byte[]> classes = classLoader instanceof MemoryClassLoader ?
+        ConcurrentHashMap<String, byte[]> classes = classLoader instanceof MemoryClassLoader ?
             ((MemoryClassLoader)classLoader).classBytes : new ConcurrentHashMap<>();
 
         try (final MemoryJavaFileManager fileManager = new MemoryJavaFileManager(cmp.getStandardFileManager(null, null, null), classes)) {
@@ -176,55 +104,124 @@ public final class MarshallerUtil {
         }
     }
 
+    /**
+     * @param iterables Iterables.
+     * @return Concated iterable.
+     */
+    private static <E> Iterable<E> concat(Iterable<? extends E>... iterables) {
+        return new Iterable<>() {
+            private final Iterator<Iterable<? extends E>> it = Arrays.asList(iterables).iterator();
+
+            /** {@inheritDoc} */
+            @Override public Iterator<E> iterator() {
+                return new Iterator<>() {
+                    private Iterator<? extends E> curIt = Collections.emptyIterator();
+
+                    /** {@inheritDoc} */
+                    @Override public boolean hasNext() {
+                        if (curIt == null || !curIt.hasNext())
+                            advance();
+
+                        return curIt.hasNext();
+                    }
+
+                    /** Switches to next iterable. */
+                    private void advance() {
+                        while (it.hasNext() && !curIt.hasNext())
+                            curIt = it.next().iterator();
+                    }
+
+                    /** {@inheritDoc} */
+                    @Override public E next() {
+                        if (!hasNext())
+                            throw new NoSuchElementException();
+
+                        return curIt.next();
+                    }
+
+                    /** {@inheritDoc} */
+                    @Override public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    /**
+     * In-memory java file manager.
+     */
     private static class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
-        private final Map<String, byte[]> classes;
+        /** Classes code. */
+        private final Map<String, byte[]> classesBytes;
 
-        public MemoryJavaFileManager(JavaFileManager fileManager, Map<String, byte[]> classes) {
+        /**
+         * Constructor.
+         *
+         * @param fileManager Java file manager.
+         * @param classesBytes Classes code.
+         */
+        public MemoryJavaFileManager(JavaFileManager fileManager, Map<String, byte[]> classesBytes) {
             super(fileManager);
-
-            this.classes = classes;
+            this.classesBytes = classesBytes;
         }
 
+        /**
+         * Java '.class' in-memory file.
+         */
         private static class MemoryJavaFileObject extends SimpleJavaFileObject {
-            private final byte[] bytes;
-
-            public MemoryJavaFileObject(String className, byte[] bytes) {
+            /** Class code. */
+            private final byte[] classBytes;
+
+            /**
+             * Constructor.
+             *
+             * @param className Class name.
+             * @param classBytes Class code.
+             */
+            MemoryJavaFileObject(String className, byte[] classBytes) {
                 super(URI.create(className + Kind.CLASS.extension), Kind.CLASS);
-                this.bytes = bytes;
+                this.classBytes = classBytes;
             }
 
+            /** {@inheritDoc} */
             @Override public InputStream openInputStream() {
-                return new ByteArrayInputStream(bytes);
+                return new ByteArrayInputStream(classBytes);
             }
         }
 
         /**
          * A file object that stores Java bytecode into the classBytes map.
          */
-        private class ClassOutputBuffer extends SimpleJavaFileObject {
+        private class JavaClassOutputFile extends SimpleJavaFileObject {
+            /** Class name. */
             private final String classname;
 
-            ClassOutputBuffer(String classname) {
+            /**
+             * Constructor.
+             *
+             * @param classname Class name.
+             */
+            JavaClassOutputFile(String classname) {
                 super(URI.create(classname), Kind.CLASS);
                 this.classname = classname;
             }
 
+            /** {@inheritDoc} */
             @Override public OutputStream openOutputStream() {
                 return new ByteArrayOutputStream() {
                     @Override public void close() throws IOException {
                         super.close();
 
-                        classes.put(classname, toByteArray());
+                        classesBytes.put(classname, toByteArray());
                     }
                 };
             }
         }
 
-        @Override public ClassLoader getClassLoader(Location location) {
-            return new MemoryClassLoader(classes,  super.getClassLoader(location));
-        }
-        @Override
-        public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds,
+        /** {@inheritDoc} */
+        @Override public Iterable<JavaFileObject> list(Location location, String packageName,
+            Set<JavaFileObject.Kind> kinds,
             boolean recurse) throws IOException {
             final Iterable<JavaFileObject> it = super.list(location, packageName, kinds, recurse);
 
@@ -233,7 +230,10 @@ public final class MarshallerUtil {
 
                 Iterable<JavaFileObject> localClasses = new Iterable<>() {
                     @Override public Iterator<JavaFileObject> iterator() {
-                        return classes.keySet().stream().map(cn -> getJavaFileObjectByName(cn)).iterator();
+                        return classesBytes.keySet().stream()
+                            .filter(cn -> cn.startsWith(packageName))
+                            .map(cn -> getJavaFileObjectByName(cn))
+                            .filter(Objects::nonNull).iterator();
                     }
                 };
 
@@ -243,6 +243,7 @@ public final class MarshallerUtil {
                 return it;
         }
 
+        /** {@inheritDoc} */
         @Override public String inferBinaryName(Location location, JavaFileObject jfo) {
             if (!(jfo instanceof MemoryJavaFileObject)) {
                 String result = super.inferBinaryName(location, jfo);
@@ -269,7 +270,9 @@ public final class MarshallerUtil {
             return bn;
         }
 
-        @Override public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
+        /** {@inheritDoc} */
+        @Override public JavaFileObject getJavaFileForInput(Location location, String className,
+            JavaFileObject.Kind kind) throws IOException {
             if (location == StandardLocation.CLASS_OUTPUT) {
                 JavaFileObject javaFileObject = getJavaFileObjectByName(className);
 
@@ -280,8 +283,9 @@ public final class MarshallerUtil {
             return fileManager.getJavaFileForInput(location, className, kind);
         }
 
+        /** {@inheritDoc} */
         @Nullable private JavaFileObject getJavaFileObjectByName(String className) {
-            final byte[] bytes = classes.get(className);
+            final byte[] bytes = classesBytes.get(className);
 
             if (bytes != null)
                 return new MemoryJavaFileObject(className, bytes);
@@ -289,28 +293,41 @@ public final class MarshallerUtil {
             return null;
         }
 
+        /** {@inheritDoc} */
         @Override public JavaFileObject getJavaFileForOutput(Location location,
             String className,
             JavaFileObject.Kind kind,
             FileObject sibling) throws IOException {
             if (kind == JavaFileObject.Kind.CLASS)
-                return new ClassOutputBuffer(className);
+                return new JavaClassOutputFile(className);
             else
                 return super.getJavaFileForOutput(location, className, kind, sibling);
         }
     }
 
+    /**
+     * Classloader for runtime compiled classes.
+     */
     private static final class MemoryClassLoader extends URLClassLoader {
+        /** Empty array. */
         private static final URL[] EMPTY_URLS = new URL[0];
 
-        private final Map<String, byte[]> classBytes;
+        /** Classes code. */
+        private final ConcurrentHashMap<String, byte[]> classBytes;
 
-        public MemoryClassLoader(Map<String, byte[]> classBytes, ClassLoader parent) {
+        /**
+         * Constructor.
+         *
+         * @param classBytes Classes code holder.
+         * @param parent Parent classloader.
+         */
+        MemoryClassLoader(ConcurrentHashMap<String, byte[]> classBytes, ClassLoader parent) {
             super(EMPTY_URLS, parent);
 
             this.classBytes = classBytes;
         }
 
+        /** {@inheritDoc} */
         @Override protected Class<?> findClass(String className) throws ClassNotFoundException {
             byte[] buf = classBytes.get(className); // clear the bytes in map -- we don't need it anymore
 
@@ -321,49 +338,7 @@ public final class MarshallerUtil {
         }
     }
 
-    public static <E> Iterable<E> concat(
-        Iterable<? extends E> i1,
-        Iterable<? extends E> i2) {
-        return new Iterable<E>() {
-            @Override public Iterator<E> iterator() {
-                return new Iterator<E>() {
-                    Iterator<? extends E> listIterator = i1.iterator();
-                    Boolean checkedHasNext;
-                    E nextValue;
-                    private boolean startTheSecond;
-
-                    void theNext() {
-                        if (listIterator.hasNext()) {
-                            checkedHasNext = true;
-                            nextValue = listIterator.next();
-                        }
-                        else if (startTheSecond)
-                            checkedHasNext = false;
-                        else {
-                            startTheSecond = true;
-                            listIterator = i2.iterator();
-                            theNext();
-                        }
-                    }
-
-                    @Override public boolean hasNext() {
-                        if (checkedHasNext == null)
-                            theNext();
-                        return checkedHasNext;
-                    }
-
-                    @Override public E next() {
-                        if (!hasNext())
-                            throw new NoSuchElementException();
-                        checkedHasNext = null;
-                        return nextValue;
-                    }
-
-                    @Override public void remove() {
-                        listIterator.remove();
-                    }
-                };
-            }
-        };
+    /** Stub. */
+    private CompilerUtils() {
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
index 4f36129..9361fa0 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
@@ -17,38 +17,11 @@
 
 package org.apache.ignite.internal.schema.marshaller;
 
-import com.squareup.javapoet.JavaFile;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.BitSet;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
 import java.util.UUID;
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticCollector;
-import javax.tools.FileObject;
-import javax.tools.ForwardingJavaFileManager;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.JavaFileObject.Kind;
-import javax.tools.SimpleJavaFileObject;
-import javax.tools.StandardLocation;
-import javax.tools.ToolProvider;
 import org.apache.ignite.internal.schema.NativeType;
 import org.apache.ignite.internal.schema.TupleAssembler;
 import org.apache.ignite.internal.util.ObjectFactory;
-import org.jetbrains.annotations.Nullable;
 
 /**
  * Marshaller utility class.
@@ -124,246 +97,19 @@ public final class MarshallerUtil {
         return null;
     }
 
-    public static <T> ObjectFactory<T> factoryForClass(Class<T> type) {
-        if (mode(type) == null)
-            return new ObjectFactory<>(type);
-        else
-            return null;
-    }
-
     /**
-     * Stub.
+     * Creates object factory for class.
+     * @param tClass Target type.
+     * @return Object factory.
      */
-    private MarshallerUtil() {
-    }
-
-    public static ClassLoader compileCode(JavaFile javafile) {
-        return compileCode(javafile, null);
-    }
-
-    public static ClassLoader compileCode(JavaFile javafile, Map<String, byte[]> classes) {
-        final JavaCompiler cmp = ToolProvider.getSystemJavaCompiler();
-
-        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-
-        Map<String, byte[]> classes = classLoader instanceof MemoryClassLoader ?
-            ((MemoryClassLoader)classLoader).classBytes : new ConcurrentHashMap<>();
-
-        try (final MemoryJavaFileManager fileManager = new MemoryJavaFileManager(cmp.getStandardFileManager(null, null, null), classes)) {
-            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
-
-            JavaCompiler.CompilationTask task = cmp.getTask(null, fileManager, diagnostics, null, null, Collections.singletonList(javafile.toJavaFileObject()));
-
-            if (task.call())
-                return classLoader instanceof MemoryClassLoader ? classLoader :
-                    new MemoryClassLoader(classes, ClassLoader.getSystemClassLoader());
-
-            // TODO: write to log.
-            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
-                System.out.println(diagnostic.getCode());
-                System.out.println(diagnostic.getKind());
-                System.out.println(diagnostic.getPosition());
-                System.out.println(diagnostic.getStartPosition());
-                System.out.println(diagnostic.getEndPosition());
-                System.out.println(diagnostic.getSource());
-                System.out.println(diagnostic.getMessage(null));
-            }
-
-            throw new IllegalStateException("Failed to compile code:\n" + javafile.toString());
-        }
-        catch (IOException ex) {
-            throw new IllegalStateException("Failed to compile code.", ex);
-        }
-    }
-
-    private static class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
-        private final Map<String, byte[]> classes;
-
-        public MemoryJavaFileManager(JavaFileManager fileManager, Map<String, byte[]> classes) {
-            super(fileManager);
-
-            this.classes = classes;
-        }
-
-        private static class MemoryJavaFileObject extends SimpleJavaFileObject {
-            private final byte[] bytes;
-
-            public MemoryJavaFileObject(String className, byte[] bytes) {
-                super(URI.create(className + Kind.CLASS.extension), Kind.CLASS);
-                this.bytes = bytes;
-            }
-
-            @Override public InputStream openInputStream() {
-                return new ByteArrayInputStream(bytes);
-            }
-        }
-
-        /**
-         * A file object that stores Java bytecode into the classBytes map.
-         */
-        private class ClassOutputBuffer extends SimpleJavaFileObject {
-            private final String classname;
-
-            ClassOutputBuffer(String classname) {
-                super(URI.create(classname), Kind.CLASS);
-                this.classname = classname;
-            }
-
-            @Override public OutputStream openOutputStream() {
-                return new ByteArrayOutputStream() {
-                    @Override public void close() throws IOException {
-                        super.close();
-
-                        classes.put(classname, toByteArray());
-                    }
-                };
-            }
-        }
-
-        @Override public ClassLoader getClassLoader(Location location) {
-            return new MemoryClassLoader(classes,  super.getClassLoader(location));
-        }
-        @Override
-        public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds,
-            boolean recurse) throws IOException {
-            final Iterable<JavaFileObject> it = super.list(location, packageName, kinds, recurse);
-
-            if (location == StandardLocation.CLASS_PATH) {
-                assert kinds.contains(JavaFileObject.Kind.CLASS);
-
-                Iterable<JavaFileObject> localClasses = new Iterable<>() {
-                    @Override public Iterator<JavaFileObject> iterator() {
-                        return classes.keySet().stream().map(cn -> getJavaFileObjectByName(cn)).iterator();
-                    }
-                };
-
-                return concat(localClasses, it);
-            }
-            else
-                return it;
-        }
-
-        @Override public String inferBinaryName(Location location, JavaFileObject jfo) {
-            if (!(jfo instanceof MemoryJavaFileObject)) {
-                String result = super.inferBinaryName(location, jfo);
-                assert result != null;
-                return result;
-            }
-
-            // A [Java]FileObject's "name" looks like this: "/orc/codehaus/commons/compiler/Foo.java".
-            // A [Java]FileObject's "binary name" looks like "java.lang.annotation.Retention".
-
-            String bn = jfo.getName();
-            if (bn.startsWith("/"))
-                bn = bn.substring(1);
-
-            if (!bn.endsWith(jfo.getKind().extension)) {
-                throw new AssertionError(
-                    "Name \"" + jfo.getName() + "\" does not match kind \"" + jfo.getKind() + "\""
-                );
-            }
-            bn = bn.substring(0, bn.length() - jfo.getKind().extension.length());
-
-            bn = bn.replace('/', '.');
-
-            return bn;
-        }
-
-        @Override public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
-            if (location == StandardLocation.CLASS_OUTPUT) {
-                JavaFileObject javaFileObject = getJavaFileObjectByName(className);
-
-                if (javaFileObject != null)
-                    return javaFileObject;
-            }
-
-            return fileManager.getJavaFileForInput(location, className, kind);
-        }
-
-        @Nullable private JavaFileObject getJavaFileObjectByName(String className) {
-            final byte[] bytes = classes.get(className);
-
-            if (bytes != null)
-                return new MemoryJavaFileObject(className, bytes);
-
+    public static <T> ObjectFactory<T> factoryForClass(Class<T> tClass) {
+        if (mode(tClass) == null)
+            return new ObjectFactory<>(tClass);
+        else
             return null;
-        }
-
-        @Override public JavaFileObject getJavaFileForOutput(Location location,
-            String className,
-            JavaFileObject.Kind kind,
-            FileObject sibling) throws IOException {
-            if (kind == JavaFileObject.Kind.CLASS)
-                return new ClassOutputBuffer(className);
-            else
-                return super.getJavaFileForOutput(location, className, kind, sibling);
-        }
     }
 
-    private static final class MemoryClassLoader extends URLClassLoader {
-        private static final URL[] EMPTY_URLS = new URL[0];
-
-        private final Map<String, byte[]> classBytes;
-
-        public MemoryClassLoader(Map<String, byte[]> classBytes, ClassLoader parent) {
-            super(EMPTY_URLS, parent);
-
-            this.classBytes = classBytes;
-        }
-
-        @Override protected Class<?> findClass(String className) throws ClassNotFoundException {
-            byte[] buf = classBytes.get(className); // clear the bytes in map -- we don't need it anymore
-
-            if (buf != null)
-                return defineClass(className, buf, 0, buf.length);
-            else
-                return super.findClass(className);
-        }
-    }
-
-    public static <E> Iterable<E> concat(
-        Iterable<? extends E> i1,
-        Iterable<? extends E> i2) {
-        return new Iterable<E>() {
-            @Override public Iterator<E> iterator() {
-                return new Iterator<E>() {
-                    Iterator<? extends E> listIterator = i1.iterator();
-                    Boolean checkedHasNext;
-                    E nextValue;
-                    private boolean startTheSecond;
-
-                    void theNext() {
-                        if (listIterator.hasNext()) {
-                            checkedHasNext = true;
-                            nextValue = listIterator.next();
-                        }
-                        else if (startTheSecond)
-                            checkedHasNext = false;
-                        else {
-                            startTheSecond = true;
-                            listIterator = i2.iterator();
-                            theNext();
-                        }
-                    }
-
-                    @Override public boolean hasNext() {
-                        if (checkedHasNext == null)
-                            theNext();
-                        return checkedHasNext;
-                    }
-
-                    @Override public E next() {
-                        if (!hasNext())
-                            throw new NoSuchElementException();
-                        checkedHasNext = null;
-                        return nextValue;
-                    }
-
-                    @Override public void remove() {
-                        listIterator.remove();
-                    }
-                };
-            }
-        };
+    /** Stub. */
+    private MarshallerUtil() {
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
index e690710..56a0cd6 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
@@ -27,7 +27,7 @@ public class SerializationException extends Exception {
      * @param cause Cause.
      */
     public SerializationException(Throwable cause) {
-        // Used by serializers generated with Janino.
+        // Used by generated serializer.
         super(cause);
     }
 
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
index 21a1560..a62400d 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
@@ -34,9 +34,11 @@ public interface Serializer {
      * @return Key object.
      */
     Object deserializeKey(byte[] data) throws SerializationException;
+//    <T> T deserializeKey(byte[] data) throws SerializationException;
 
     /**
      * @return Value object.
      */
     Object deserializeValue(byte[] data) throws SerializationException;
+//    <T> T deserializeValue(byte[] data) throws SerializationException;
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
index ce88b18..63b4f73 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
@@ -29,7 +29,7 @@ public interface SerializerFactory {
     /**
      * @return Serializer factory back by code generator.
      */
-    public static SerializerFactory createJaninoSerializerFactory() {
+    public static SerializerFactory createGeneratedSerializerFactory() {
         return new SerializerGenerator();
     }
 
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
index 9d292c7..5778a5a 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
@@ -24,32 +24,34 @@ import org.apache.ignite.internal.schema.marshaller.Serializer;
 /**
  * Generate {@link Serializer} method's bodies for simple types.
  */
-class IdentityObjectMarshallerExprGenerator extends MarshallerExprGenerator {
+class IdentityObjectMarshallerExprGenerator implements MarshallerCodeGenerator {
+    /** Tuple column accessor. */
+    private final TupleColumnAccessCodeGenerator columnAccessor;
+
     /**
      * Constructor.
      *
-     * @param aClass
-     * @param tupleAccessorGen Object field access expression generators.
+     * @param columnAccessor Tuple column code generator.
      */
-    IdentityObjectMarshallerExprGenerator(Class<?> aClass, TupleAccessorExpr tupleAccessorGen) {
-        super(aClass, null /* no instantiation needed */, new TupleAccessorExpr[] {tupleAccessorGen});
+    IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator columnAccessor) {
+        this.columnAccessor = columnAccessor;
     }
 
     /** {@inheritDoc} */
-    @Override boolean isSimpleType() {
+    @Override public boolean isSimpleType() {
         return true;
     }
 
     /** {@inheritDoc} */
     @Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
         return CodeBlock.builder()
-            .add("return ").addStatement(tupleAccessors[0].read(tupleExpr))
+            .addStatement("return $L", columnAccessor.read(tupleExpr))
             .build();
     }
 
     /** {@inheritDoc} */
     @Override public CodeBlock marshallObjectCode(String asm, String objVar) {
-        return tupleAccessors[0].write(asm, objVar);
+        return columnAccessor.write(asm, objVar);
     }
 
     /** {@inheritDoc} */
@@ -58,7 +60,7 @@ class IdentityObjectMarshallerExprGenerator extends MarshallerExprGenerator {
     }
 
     /** {@inheritDoc} */
-    @Override public void staticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
-
+    @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+        throw new UnsupportedOperationException("Static handlers are not applicable to simple types.");
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java
new file mode 100644
index 0000000..bca3256
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java
@@ -0,0 +1,47 @@
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeSpec;
+
+/**
+ * Marshaller code generator.
+ */
+interface MarshallerCodeGenerator {
+    /**
+     * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
+     */
+    boolean isSimpleType();
+
+    /**
+     * @param tupleExpr Tuple to read from.
+     * @return Unmarshall object code.
+     */
+    CodeBlock unmarshallObjectCode(String tupleExpr);
+
+    /**
+     * @param asm Tuple assembler to write to.
+     * @param objVar Object to serialize.
+     * @return Marshall object code.
+     */
+    CodeBlock marshallObjectCode(String asm, String objVar);
+
+    /**
+     * @param objVar Object var.
+     * @param colIdx Column index.
+     * @return Object field value for given column.
+     */
+    CodeBlock getValueCode(String objVar, int colIdx);
+
+    /**
+     * @param classBuilder Class builder.
+     * @param staticInitBuilder Static initializer builder.
+     */
+    void initStaticHandlers(TypeSpec.Builder classBuilder, CodeBlock.Builder staticInitBuilder);
+
+    /**
+     * @return Marshaller target class.
+     */
+    default Class<?> getClazz() {
+        return null;
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java
similarity index 53%
rename from modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
rename to modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java
index 53be5ed..6a54d4d 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java
@@ -28,36 +28,40 @@ import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
 import org.apache.ignite.internal.schema.marshaller.Serializer;
 
 /**
- * Generate {@link Serializer} method's bodies.
+ * Generates {@link Serializer} methods code.
  */
-class MarshallerExprGenerator {
-    /** Object factory regerence expression. */
-    private final String factoryRefExpr;
+class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
+    /** Target object factory var. */
+    private final String objectFactoryVar;
 
+    /** Target class. */
     private final Class<?> tClass;
 
+    /** Mapped columns. */
     private final Columns columns;
 
     /** Object field access expression generators. */
-    protected TupleAccessorExpr[] tupleAccessors;
+    private final TupleColumnAccessCodeGenerator[] columnAccessessors;
 
     /**
      * Constructor.
      *
-     * @param tClass
-     * @param factoryRefExpr Object factory regerence expression.
+     * @param tClass Target object class.
+     * @param objectFactoryVar Target object factory var.
+     * @param columns Column object is mapped to.
+     * @param firstColIdx Column index offset.
      */
-    public MarshallerExprGenerator(Class<?> tClass, String factoryRefExpr, Columns columns, int firstColIdx) {
-        this.factoryRefExpr = factoryRefExpr;
+    public ObjectMarshallerCodeGenerator(Class<?> tClass, String objectFactoryVar, Columns columns, int firstColIdx) {
+        this.objectFactoryVar = objectFactoryVar;
         this.tClass = tClass;
 
         this.columns = columns;
-        tupleAccessors = new TupleAccessorExpr[this.columns.length()];
+        columnAccessessors = new TupleColumnAccessCodeGenerator[this.columns.length()];
         try {
             for (int i = 0; i < columns.length(); i++) {
                 final Field field = tClass.getDeclaredField(columns.column(i).name());
 
-                tupleAccessors[i] = TupleAccessorExpr.createAccessor(MarshallerUtil.mode(field.getType()), i + firstColIdx);
+                columnAccessessors[i] = TupleColumnAccessCodeGenerator.createAccessor(MarshallerUtil.mode(field.getType()), i + firstColIdx);
             }
         }
         catch (NoSuchFieldException ex) {
@@ -65,60 +69,56 @@ class MarshallerExprGenerator {
         }
     }
 
-    public MarshallerExprGenerator(Class<?> tClass, Object expr, TupleAccessorExpr[] tupleAccessorExprs) {
-        this.factoryRefExpr = null;
-        this.tClass = tClass;
-        columns = null;
-        this.tupleAccessors = tupleAccessorExprs;
+    /** {@inheritDoc} */
+    @Override public Class<?> getClazz() {
+        return tClass;
     }
 
-    /**
-     * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
-     */
-    boolean isSimpleType() {
+    /** {@inheritDoc} */
+    @Override public boolean isSimpleType() {
         return false;
     }
 
-    Class<?> getClazz() {
-        return tClass;
-    }
-
-    public CodeBlock unmarshallObjectCode(String tupleExpr) {
+    /** {@inheritDoc} */
+    @Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
         final CodeBlock.Builder builder = CodeBlock.builder()
-            .add("Object obj = ").add(factoryRefExpr).addStatement(".create()");
+            .addStatement("$T obj = ($T)$L.create()", tClass, tClass, objectFactoryVar);
 
-        for (int i = 0; i < tupleAccessors.length; i++)
-            builder.addStatement("FIELD_HANDLE_$L.set(obj, $L)", tupleAccessors[i].colIdx, tupleAccessors[i].read(tupleExpr).toString());
+        for (int i = 0; i < columnAccessessors.length; i++)
+            builder.addStatement("FIELD_HANDLE_$L.set(obj, $L)", columnAccessessors[i].columnIdx(), columnAccessessors[i].read(tupleExpr).toString());
 
         builder.addStatement("return obj");
         return builder.build();
     }
 
-    public CodeBlock getValueCode(String objVar, int i) {
-        return CodeBlock.of("FIELD_HANDLE_$L.get($L)", tupleAccessors[i].colIdx, objVar);
+    /** {@inheritDoc} */
+    @Override public CodeBlock getValueCode(String objVar, int i) {
+        return CodeBlock.of("FIELD_HANDLE_$L.get($L)", columnAccessessors[i].columnIdx(), objVar);
     }
 
-    public CodeBlock marshallObjectCode(String asm, String objVar) {
+    /** {@inheritDoc} */
+    @Override public CodeBlock marshallObjectCode(String asm, String objVar) {
         final CodeBlock.Builder builder = CodeBlock.builder();
 
-        for (int i = 0; i < tupleAccessors.length; i++)
-            builder.addStatement(tupleAccessors[i].write(asm, getValueCode(objVar, i).toString()).toString());
+        for (int i = 0; i < columnAccessessors.length; i++)
+            builder.add(columnAccessessors[i].write(asm, getValueCode(objVar, i).toString()));
 
         return builder.build();
     }
 
-    public void staticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
-        for (int i = 0; i < tupleAccessors.length; i++) {
+    /** {@inheritDoc} */
+    @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+        for (int i = 0; i < columnAccessessors.length; i++) {
             builder.addField(FieldSpec.builder(
                 VarHandle.class,
-                CodeBlock.of("FIELD_HANDLE_$L", tupleAccessors[i].colIdx).toString(),
+                CodeBlock.of("FIELD_HANDLE_$L", columnAccessessors[i].columnIdx()).toString(),
                 Modifier.PRIVATE,
                 Modifier.FINAL,
                 Modifier.STATIC)
                 .build());
 
             staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($T.class.getDeclaredField($S))",
-                tupleAccessors[i].colIdx, tClass, columns.column(i).name());
+                columnAccessessors[i].columnIdx(), tClass, columns.column(i).name());
         }
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java
index 42786bf..c43406b 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java
@@ -26,6 +26,7 @@ import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
 import javax.annotation.processing.Generated;
 import javax.lang.model.element.Modifier;
 import org.apache.ignite.internal.schema.ByteBufferTuple;
@@ -34,22 +35,23 @@ import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.Tuple;
 import org.apache.ignite.internal.schema.TupleAssembler;
 import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.CompilerUtils;
 import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
 import org.apache.ignite.internal.schema.marshaller.SerializationException;
 import org.apache.ignite.internal.schema.marshaller.Serializer;
 import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
 import org.apache.ignite.internal.util.ObjectFactory;
-import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 /**
- * {@link Serializer} code generator backed with Janino.
+ * {@link Serializer} code generator.
  */
 public class SerializerGenerator implements SerializerFactory {
-
+    /** Serializer package name. */
     public static final String SERIALIZER_PACKAGE_NAME = "org.apache.ignite.internal.schema.marshaller";
 
-    public static final String SERIALIZER_CLASS_NAME_PREFIX = "JaninoSerializerForSchema_";
+    /** Serializer package name prefix. */
+    public static final String SERIALIZER_CLASS_NAME_PREFIX = "SerializerForSchema_";
 
     /** {@inheritDoc} */
     @Override public Serializer create(
@@ -57,26 +59,26 @@ public class SerializerGenerator implements SerializerFactory {
         Class<?> keyClass,
         Class<?> valClass
     ) {
-        // Generate Serializer code.
         final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
+        try {
+            // Generate Serializer code.
+            JavaFile javaFile = generateSerializerClassCode(className, schema, keyClass, valClass);
 
-        JavaFile javaFile = generateSerializerClassCode(className, schema, keyClass, valClass);
-
-        //TODO: pass code to logger on trace level.
-        System.out.println(javaFile.toString());
-
-        final ClassLoader loader = MarshallerUtil.compileCode(javaFile);
+            //TODO: pass code to logger on trace level.
+//            System.out.println(javaFile.toString());
 
-        try {
-            final Class<?> aClass = loader.loadClass(javaFile.packageName + '.' + className);
+            // Compile.
+            ClassLoader loader = CompilerUtils.compileCode(javaFile);
 
-            return (Serializer)aClass
+            // Instantiate serializer.
+            return (Serializer)loader.loadClass(javaFile.packageName + '.' + className)
                 .getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
                 .newInstance(schema, keyClass, valClass);
 
         }
         catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException(e);
+            throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
+                ", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName(), e);
         }
     }
 
@@ -84,15 +86,17 @@ public class SerializerGenerator implements SerializerFactory {
      * Generates serializer code.
      *
      * @param className Serializer class name.
+     * @param schema Schema descriptor.
+     * @param keyClass Key class.
      * @param valClass Value class.
-     * @return Generated class code.
+     * @return Generated java file representation.
      */
     private JavaFile generateSerializerClassCode(String className, SchemaDescriptor schema, Class<?> keyClass,
         Class<?> valClass) {
         try {
-            // Build field accessor generators.
-            final MarshallerExprGenerator keyMarsh = createObjectMarshaller(keyClass, "keyFactory", schema.keyColumns(), 0);
-            final MarshallerExprGenerator valMarsh = createObjectMarshaller(valClass, "valFactory", schema.valueColumns(), schema.keyColumns().length());
+            // Build code generators.
+            final MarshallerCodeGenerator keyMarsh = createObjectMarshaller(keyClass, "keyFactory", schema.keyColumns(), 0);
+            final MarshallerCodeGenerator valMarsh = createObjectMarshaller(valClass, "valFactory", schema.valueColumns(), schema.keyColumns().length());
 
             final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
                 .addSuperinterface(Serializer.class)
@@ -115,14 +119,14 @@ public class SerializerGenerator implements SerializerFactory {
                         .addStatement("this.valFactory = $T.factoryForClass(valClass)", MarshallerUtil.class)
                         .build()
                 )
-                .addMethod(generateHelpersMetod(schema, keyMarsh, valMarsh))
+                .addMethod(generateTupleAsseblerFactoryMethod(schema, keyMarsh, valMarsh))
                 .addMethod(generateSerializeMethod(keyMarsh, valMarsh))
                 .addMethod(generateDeserializeKeyMethod(keyMarsh))
                 .addMethod(generateDeserializeValueMethod(valMarsh));
 
             return JavaFile
                 .builder(SERIALIZER_PACKAGE_NAME, classBuilder.build())
-                .addStaticImport(TupleAccessorExpr.class, "*")
+                .addStaticImport(TupleColumnAccessCodeGenerator.class, "*")
                 .addStaticImport(MethodHandles.class, "Lookup")
                 .skipJavaLangImports(true)
                 .indent("    ")
@@ -134,34 +138,65 @@ public class SerializerGenerator implements SerializerFactory {
         }
     }
 
-    private void initFieldHandlers(MarshallerExprGenerator keyMarsh, MarshallerExprGenerator valMarsh,
-        TypeSpec.Builder classBuilder) {
+    /**
+     * @param keyMarsh Key marshaller code generator.
+     * @param valMarsh Value marshaller code generator.
+     * @param classBuilder Serializer class builder.
+     */
+    private void initFieldHandlers(
+        MarshallerCodeGenerator keyMarsh,
+        MarshallerCodeGenerator valMarsh,
+        TypeSpec.Builder classBuilder
+    ) {
         if (keyMarsh.isSimpleType() && valMarsh.isSimpleType())
-            return;
+            return; // No field hanlders needed for simple types.
 
         final CodeBlock.Builder staticInitBuilder = CodeBlock.builder()
             .addStatement("$T.Lookup lookup", MethodHandles.class)
             .beginControlFlow("try");
 
         if (!keyMarsh.isSimpleType()) {
-            staticInitBuilder.addStatement("lookup = $T.privateLookupIn($T.class, $T.lookup())", MethodHandles.class, keyMarsh.getClazz(), MethodHandles.class);
-            keyMarsh.staticHandlers(classBuilder, staticInitBuilder);
+            staticInitBuilder.addStatement(
+                "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+                MethodHandles.class,
+                Objects.requireNonNull(keyMarsh.getClazz()),
+                MethodHandles.class
+            );
+
+            keyMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
         }
 
         if (!valMarsh.isSimpleType()) {
-            staticInitBuilder.addStatement("lookup = $T.privateLookupIn($T.class, $T.lookup())", MethodHandles.class, valMarsh.getClazz(), MethodHandles.class);
-            valMarsh.staticHandlers(classBuilder, staticInitBuilder);
+            staticInitBuilder.addStatement(
+                "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+                MethodHandles.class,
+                Objects.requireNonNull(valMarsh.getClazz()),
+                MethodHandles.class
+            );
+
+            valMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
         }
 
-        staticInitBuilder.nextControlFlow("catch ($T | $T ex)", ReflectiveOperationException.class, SecurityException.class)
+        staticInitBuilder
+            .nextControlFlow(
+                "catch ($T | $T ex)",
+                ReflectiveOperationException.class,
+                SecurityException.class
+            )
             .addStatement("throw new $T(ex)", IllegalStateException.class)
             .endControlFlow();
 
         classBuilder.addStaticBlock(staticInitBuilder.build());
     }
 
-    private MethodSpec generateHelpersMetod(SchemaDescriptor schema, MarshallerExprGenerator keyMarsh,
-        MarshallerExprGenerator valMarsh) {
+    /**
+     * @param schema Schema descriptor.
+     * @param keyMarsh Key marshaller code generator.
+     * @param valMarsh Value marshaller code generator.
+     * @return Tuple accembler factory method spec.
+     */
+    private MethodSpec generateTupleAsseblerFactoryMethod(SchemaDescriptor schema, MarshallerCodeGenerator keyMarsh,
+        MarshallerCodeGenerator valMarsh) {
         final MethodSpec.Builder builder = MethodSpec
             .methodBuilder("createAssembler")
             .addModifiers(Modifier.PRIVATE)
@@ -169,8 +204,8 @@ public class SerializerGenerator implements SerializerFactory {
             .addParameter(Object.class, "val")
             .returns(TupleAssembler.class)
 
-            .addStatement("int nonNullVarlenKeys = 0; int nonNullVarlenValues = 0")
-            .addStatement("int nonNullVarlenKeysSize = 0; int nonNullVarlenValuesSize = 0")
+            .addStatement("int varlenKeyCols = 0; int varlenValueCols = 0")
+            .addStatement("int varlenKeyColsSize = 0; int varlenValueColsSize = 0")
             .addStatement("$T keyCols = schema.keyColumns()", Columns.class)
             .addStatement("$T valCols = schema.valueColumns()", Columns.class);
 
@@ -185,8 +220,8 @@ public class SerializerGenerator implements SerializerFactory {
                 block.addStatement("fVal = $L", keyMarsh.getValueCode("key", i).toString());
 
                 block.beginControlFlow("if (fVal != null)")
-                    .addStatement("nonNullVarlenKeysSize += $T.getValueSize(fVal, keyCols.column($L).type())", MarshallerUtil.class, i)
-                    .addStatement("nonNullVarlenKeys++")
+                    .addStatement("varlenKeyColsSize += $T.getValueSize(fVal, keyCols.column($L).type())", MarshallerUtil.class, i)
+                    .addStatement("varlenKeyCols++")
                     .endControlFlow();
             }
             block.unindent();
@@ -208,8 +243,8 @@ public class SerializerGenerator implements SerializerFactory {
                 block.addStatement("fVal = $L", valMarsh.getValueCode("val", i).toString());
 
                 block.beginControlFlow("if (fVal != null)")
-                    .addStatement("nonNullVarlenValuesSize += $T.getValueSize(fVal, valCols.column($L).type())", MarshallerUtil.class, i)
-                    .addStatement("nonNullVarlenValues++")
+                    .addStatement("varlenValueColsSize += $T.getValueSize(fVal, valCols.column($L).type())", MarshallerUtil.class, i)
+                    .addStatement("varlenValueCols++")
                     .endControlFlow();
             }
             block.unindent();
@@ -220,15 +255,19 @@ public class SerializerGenerator implements SerializerFactory {
                 .addCode("}\n");
         }
 
-        builder.addStatement("int size = $T.tupleSize(keyCols, nonNullVarlenKeys, nonNullVarlenKeysSize," +
-            "valCols, nonNullVarlenValues, nonNullVarlenValuesSize)", TupleAssembler.class);
+        builder.addStatement("int size = $T.tupleSize(keyCols, varlenKeyCols, varlenKeyColsSize," +
+            "valCols, varlenValueCols, varlenValueColsSize)", TupleAssembler.class);
 
-        builder.addStatement("return new $T(schema, size, nonNullVarlenKeys, nonNullVarlenValues)", TupleAssembler.class);
+        builder.addStatement("return new $T(schema, size, varlenKeyCols, varlenValueCols)", TupleAssembler.class);
 
         return builder.build();
     }
 
-    @NotNull private MethodSpec generateDeserializeValueMethod(MarshallerExprGenerator valMarsh) {
+    /**
+     * @param valMarsh Value marshaller code generator.
+     * @return Deserialize value method spec.
+     */
+    private MethodSpec generateDeserializeValueMethod(MarshallerCodeGenerator valMarsh) {
         return MethodSpec
             .methodBuilder("deserializeValue")
             .addAnnotation(Override.class)
@@ -246,7 +285,11 @@ public class SerializerGenerator implements SerializerFactory {
             .endControlFlow().build();
     }
 
-    @NotNull private MethodSpec generateDeserializeKeyMethod(MarshallerExprGenerator keyMarsh) {
+    /**
+     * @param keyMarsh Key marshaller code generator.
+     * @return Deserialize key method spec.
+     */
+    private MethodSpec generateDeserializeKeyMethod(MarshallerCodeGenerator keyMarsh) {
         return MethodSpec
             .methodBuilder("deserializeKey")
             .addAnnotation(Override.class)
@@ -264,8 +307,12 @@ public class SerializerGenerator implements SerializerFactory {
             .endControlFlow().build();
     }
 
-    @NotNull
-    private MethodSpec generateSerializeMethod(MarshallerExprGenerator keyMarsh, MarshallerExprGenerator valMarsh) {
+    /**
+     * @param keyMarsh Key marshaller code generator.
+     * @param valMarsh Value marshaller code generator.
+     * @return Serialize method spec.
+     */
+    private MethodSpec generateSerializeMethod(MarshallerCodeGenerator keyMarsh, MarshallerCodeGenerator valMarsh) {
         return MethodSpec.
             methodBuilder("serialize")
             .addAnnotation(Override.class)
@@ -288,25 +335,25 @@ public class SerializerGenerator implements SerializerFactory {
     }
 
     /**
-     * Creates marshal/unmarshall expressions generator for object.
+     * Creates marshaller code generator for given class.
      *
-     * @param aClass Object class.
-     * @param factoryRefExpr Factory reference expression.
-     * @param columns Columns that aClass mapped to.
+     * @param tClass Target class.
+     * @param factoryRefVar Object factory variable.
+     * @param columns Columns that tClass mapped to.
      * @param firstColIdx First column absolute index in schema.
-     * @return Marshal/unmarshall expression generator.
+     * @return Marshaller code generator.
      */
-    private MarshallerExprGenerator createObjectMarshaller(
-        Class<?> aClass,
-        @Nullable String factoryRefExpr,
+    private MarshallerCodeGenerator createObjectMarshaller(
+        Class<?> tClass,
+        @Nullable String factoryRefVar,
         Columns columns,
         int firstColIdx
     ) {
-        BinaryMode mode = MarshallerUtil.mode(aClass);
+        BinaryMode mode = MarshallerUtil.mode(tClass);
 
-        if (mode != null)
-            return new IdentityObjectMarshallerExprGenerator(aClass, TupleAccessorExpr.createAccessor(mode, firstColIdx));
+        if (mode != null) // Simple type.
+            return new IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
         else
-            return new MarshallerExprGenerator(aClass, factoryRefExpr, columns, firstColIdx);
+            return new ObjectMarshallerCodeGenerator(tClass, factoryRefVar, columns, firstColIdx);
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleAccessorExpr.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java
similarity index 55%
rename from modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleAccessorExpr.java
rename to modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java
index ccca864..968f24c 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleAccessorExpr.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java
@@ -28,41 +28,71 @@ import org.apache.ignite.internal.schema.TupleAssembler;
 import org.apache.ignite.internal.schema.marshaller.BinaryMode;
 
 /**
- * Object field access expression generators.
+ * Tuple access code generator.
  */
-public class TupleAccessorExpr {
+public class TupleColumnAccessCodeGenerator {
+    /** Tuple method handler. */
     public static final MethodHandle READ_BYTE;
+    /** Tuple method handler. */
     public static final MethodHandle READ_SHORT;
+    /** Tuple method handler. */
     public static final MethodHandle READ_INT;
+    /** Tuple method handler. */
     public static final MethodHandle READ_LONG;
+    /** Tuple method handler. */
     public static final MethodHandle READ_FLOAT;
+    /** Tuple method handler. */
     public static final MethodHandle READ_DOUBLE;
+    /** Tuple method handler. */
     public static final MethodHandle READ_BYTE_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_SHORT_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_INT_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_LONG_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_FLOAT_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_DOUBLE_BOXED;
+    /** Tuple method handler. */
     public static final MethodHandle READ_UUID;
+    /** Tuple method handler. */
     public static final MethodHandle READ_BITSET;
+    /** Tuple method handler. */
     public static final MethodHandle READ_STRING;
+    /** Tuple method handler. */
     public static final MethodHandle READ_BYTE_ARR;
 
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_NULL;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_BYTE;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_SHORT;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_INT;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_LONG;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_FLOAT;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_DOUBLE;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_UUID;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_BITSET;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_STRING;
+    /** Tuple assembler method handler. */
     public static final MethodHandle WRITE_BYTE_ARR;
 
+    /**
+     * Initializes static handlers.
+     */
     static {
         try {
-            MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Tuple.class, MethodHandles.lookup());
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
 
             READ_BYTE = lookup.findVirtual(Tuple.class, "byteValue", MethodType.methodType(byte.class, int.class));
             READ_SHORT = lookup.findVirtual(Tuple.class, "shortValue", MethodType.methodType(short.class, int.class));
@@ -81,7 +111,6 @@ public class TupleAccessorExpr {
             READ_STRING = lookup.findVirtual(Tuple.class, "stringValue", MethodType.methodType(String.class, int.class));
             READ_BYTE_ARR = lookup.findVirtual(Tuple.class, "bytesValue", MethodType.methodType(byte[].class, int.class));
 
-            lookup = MethodHandles.privateLookupIn(TupleAssembler.class, MethodHandles.lookup());
             WRITE_NULL = lookup.findVirtual(TupleAssembler.class, "appendNull", MethodType.methodType(void.class));
             WRITE_BYTE = lookup.findVirtual(TupleAssembler.class, "appendByte", MethodType.methodType(void.class, byte.class));
             WRITE_SHORT = lookup.findVirtual(TupleAssembler.class, "appendShort", MethodType.methodType(void.class, short.class));
@@ -97,76 +126,128 @@ public class TupleAccessorExpr {
         catch (NoSuchMethodException | IllegalAccessException e) {
             throw new IllegalStateException(e);
         }
-
     }
 
-    private final Class<?> rType;
-
-    static TupleAccessorExpr createAccessor(BinaryMode mode, int colIdx) {
+    /**
+     * @param mode Binary mode.
+     * @param colIdx Column index in schema.
+     * @return Tuple column access code generator.
+     */
+    static TupleColumnAccessCodeGenerator createAccessor(BinaryMode mode, int colIdx) {
         switch (mode) {
             case P_BYTE:
-                return new TupleAccessorExpr("READ_BYTE", "WRITE_BYTE", byte.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_BYTE", "WRITE_BYTE", byte.class, colIdx);
             case P_SHORT:
-                return new TupleAccessorExpr("READ_SHORT", "WRITE_SHORT", short.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_SHORT", "WRITE_SHORT", short.class, colIdx);
             case P_INT:
-                return new TupleAccessorExpr("READ_INT", "WRITE_INT", int.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_INT", "WRITE_INT", int.class, colIdx);
             case P_LONG:
-                return new TupleAccessorExpr("READ_LONG", "WRITE_LONG", long.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_LONG", "WRITE_LONG", long.class, colIdx);
             case P_FLOAT:
-                return new TupleAccessorExpr("READ_FLOAT", "WRITE_FLOAT", float.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_FLOAT", "WRITE_FLOAT", float.class, colIdx);
             case P_DOUBLE:
-                return new TupleAccessorExpr("READ_DOUBLE", "WRITE_DOUBLE", double.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_DOUBLE", "WRITE_DOUBLE", double.class, colIdx);
             case BYTE:
-                return new TupleAccessorExpr("READ_BYTE_BOXED", "WRITE_BYTE", Byte.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_BYTE_BOXED", "WRITE_BYTE", Byte.class, byte.class, colIdx);
             case SHORT:
-                return new TupleAccessorExpr("READ_SHORT_BOXED", "WRITE_SHORT", Short.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_SHORT_BOXED", "WRITE_SHORT", Short.class, short.class, colIdx);
             case INT:
-                return new TupleAccessorExpr("READ_INT_BOXED", "WRITE_INT", Integer.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_INT_BOXED", "WRITE_INT", Integer.class, int.class, colIdx);
             case LONG:
-                return new TupleAccessorExpr("READ_LONG_BOXED", "WRITE_LONG", Long.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_LONG_BOXED", "WRITE_LONG", Long.class, long.class, colIdx);
             case FLOAT:
-                return new TupleAccessorExpr("READ_FLOAT_BOXED", "WRITE_FLOAT", Float.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_FLOAT_BOXED", "WRITE_FLOAT", Float.class, float.class, colIdx);
             case DOUBLE:
-                return new TupleAccessorExpr("READ_DOUBLE_BOXED", "WRITE_DOUBLE", Double.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_DOUBLE_BOXED", "WRITE_DOUBLE", Double.class, double.class, colIdx);
             case STRING:
-                return new TupleAccessorExpr("READ_STRING", "WRITE_STRING", String.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_STRING", "WRITE_STRING", String.class, colIdx);
             case UUID:
-                return new TupleAccessorExpr("READ_UUID", "WRITE_UUID", UUID.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_UUID", "WRITE_UUID", UUID.class, colIdx);
             case BYTE_ARR:
-                return new TupleAccessorExpr("READ_BYTE_ARR", "WRITE_BYTE_ARR", byte[].class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_BYTE_ARR", "WRITE_BYTE_ARR", byte[].class, colIdx);
             case BITSET:
-                return new TupleAccessorExpr("READ_BITSET", "WRITE_BITSET", BitSet.class, colIdx);
+                return new TupleColumnAccessCodeGenerator("READ_BITSET", "WRITE_BITSET", BitSet.class, colIdx);
         }
 
         throw new IllegalStateException("Unsupported binary mode: " + mode);
     }
 
-    private final String readExpr;
-    private final String writeExpr;
-    final int colIdx;
+    /** Reader handle name. */
+    private final String readHandleName;
+
+    /** Writer handle name. */
+    private final String writeHandleName;
+
+    /** Mapped value type. */
+    private final Class<?> mappedType;
+
+    /** Write method argument type. */
+    private final Class<?> writeArgType;
 
-    private TupleAccessorExpr(String readExpr, String writeExpr, Class<?> rType, int colIdx) {
-        this.readExpr = readExpr;
-        this.writeExpr = writeExpr;
+    /** Column index in schema. */
+    private final int colIdx;
+
+    /**
+     * Constructor.
+     *
+     * @param readHandleName Reader handle name.
+     * @param writeHandleName Writer handle name.
+     * @param mappedType Mapped value type.
+     * @param colIdx Column index in schema.
+     */
+    TupleColumnAccessCodeGenerator(String readHandleName, String writeHandleName, Class<?> mappedType, int colIdx) {
+        this(readHandleName, writeHandleName, mappedType, mappedType, colIdx);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param readHandleName Reader handle name.
+     * @param writeHandleName Writer handle name.
+     * @param mappedType Mapped value type.
+     * @param writeArgType Write method argument type.
+     * @param colIdx Column index in schema.
+     */
+    TupleColumnAccessCodeGenerator(String readHandleName, String writeHandleName, Class<?> mappedType,
+        Class<?> writeArgType, int colIdx) {
+        this.readHandleName = readHandleName;
+        this.writeHandleName = writeHandleName;
         this.colIdx = colIdx;
-        this.rType = rType;
+        this.mappedType = mappedType;
+        this.writeArgType = writeArgType;
+    }
+
+    /**
+     * @return Column index in schema.
+     */
+    public int columnIdx() {
+        return colIdx;
     }
 
+    /**
+     * @param tuple Tuple.
+     * @return Code that reads column value from tuple.
+     */
     public CodeBlock read(String tuple) {
-        return CodeBlock.of("($T)$T.$L.invoke($L, $L)", rType, TupleAccessorExpr.class, readExpr, tuple, colIdx);
+        return CodeBlock.of("($T)$T.$L.invoke($L, $L)", mappedType, TupleColumnAccessCodeGenerator.class, readHandleName, tuple, colIdx);
     }
 
+    /**
+     * @param asmVar Tuple assembler var.
+     * @param valExpr Value expression.
+     * @return Code that writes value to tuple column.
+     */
     public CodeBlock write(String asmVar, String valExpr) {
-        if (rType.isPrimitive())
-            return CodeBlock.of("$T.$L.invoke($L, $L)", TupleAccessorExpr.class, writeExpr, asmVar, valExpr);
+        if (mappedType.isPrimitive())
+            return CodeBlock.builder().addStatement("$T.$L.invokeExact($L, ($T)$L)", TupleColumnAccessCodeGenerator.class, writeHandleName, asmVar, writeArgType, valExpr).build();
         else {
             return CodeBlock.builder()
                 .add("{\n").indent()
-                .addStatement("$T fVal", rType)
-                .beginControlFlow("if((fVal = ($T)$L) == null)", rType, valExpr)
-                .addStatement("$T.WRITE_NULL.invoke($L)", TupleAccessorExpr.class, asmVar)
+                .addStatement("$T fVal", mappedType)
+                .beginControlFlow("if((fVal = ($T)$L) == null)", mappedType, valExpr)
+                .addStatement("$T.WRITE_NULL.invokeExact($L)", TupleColumnAccessCodeGenerator.class, asmVar)
                 .nextControlFlow("else")
-                .addStatement("$T.$L.invoke($L, fVal)", TupleAccessorExpr.class, writeExpr, asmVar)
+                .addStatement("$T.$L.invokeExact($L, ($T)fVal)", TupleColumnAccessCodeGenerator.class, writeHandleName, asmVar, writeArgType)
                 .endControlFlow()
                 .unindent()
                 .add("}\n")
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
index 812b02e..6cad0d0 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
@@ -20,7 +20,9 @@ package org.apache.ignite.internal.schema.marshaller.reflection;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import java.lang.reflect.Field;
+import java.util.BitSet;
 import java.util.Objects;
+import java.util.UUID;
 import org.apache.ignite.internal.schema.Column;
 import org.apache.ignite.internal.schema.Columns;
 import org.apache.ignite.internal.schema.Tuple;
@@ -148,6 +150,150 @@ public abstract class FieldAccessor {
     }
 
     /**
+     * Reads value object from tuple.
+     *
+     * @param reader Reader.
+     * @param colIdx Column index.
+     * @param mode Binary read mode.
+     * @return Read value object.
+     */
+    private static Object readRefValue(Tuple reader, int colIdx, BinaryMode mode) {
+        assert reader != null;
+        assert colIdx >= 0;
+
+        Object val = null;
+
+        switch (mode) {
+            case BYTE:
+                val = reader.byteValueBoxed(colIdx);
+
+                break;
+
+            case SHORT:
+                val = reader.shortValueBoxed(colIdx);
+
+                break;
+
+            case INT:
+                val = reader.intValueBoxed(colIdx);
+
+                break;
+
+            case LONG:
+                val = reader.longValueBoxed(colIdx);
+
+                break;
+
+            case FLOAT:
+                val = reader.floatValueBoxed(colIdx);
+
+                break;
+
+            case DOUBLE:
+                val = reader.doubleValueBoxed(colIdx);
+
+                break;
+
+            case STRING:
+                val = reader.stringValue(colIdx);
+
+                break;
+
+            case UUID:
+                val = reader.uuidValue(colIdx);
+
+                break;
+
+            case BYTE_ARR:
+                val = reader.bytesValue(colIdx);
+
+                break;
+
+            case BITSET:
+                val = reader.bitmaskValue(colIdx);
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+        }
+
+        return val;
+    }
+
+    /**
+     * Writes reference value to tuple.
+     *
+     * @param val Value object.
+     * @param writer Writer.
+     * @param mode Write binary mode.
+     */
+    private static void writeRefObject(Object val, TupleAssembler writer, BinaryMode mode) {
+        assert writer != null;
+
+        if (val == null) {
+            writer.appendNull();
+
+            return;
+        }
+
+        switch (mode) {
+            case BYTE:
+                writer.appendByte((Byte)val);
+
+                break;
+
+            case SHORT:
+                writer.appendShort((Short)val);
+
+                break;
+
+            case INT:
+                writer.appendInt((Integer)val);
+
+                break;
+
+            case LONG:
+                writer.appendLong((Long)val);
+
+                break;
+
+            case FLOAT:
+                writer.appendFloat((Float)val);
+
+                break;
+
+            case DOUBLE:
+                writer.appendDouble((Double)val);
+
+                break;
+
+            case STRING:
+                writer.appendString((String)val);
+
+                break;
+
+            case UUID:
+                writer.appendUuid((UUID)val);
+
+                break;
+
+            case BYTE_ARR:
+                writer.appendBytes((byte[])val);
+
+                break;
+
+            case BITSET:
+                writer.appendBitmask((BitSet)val);
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+        }
+    }
+
+    /**
      * Protected constructor.
      *
      * @param varHandle Field.
@@ -265,7 +411,7 @@ public abstract class FieldAccessor {
 
         /** {@inheritDoc} */
         @Override protected void write0(Object obj, TupleAssembler writer) {
-            JavaSerializer.writeRefObject(Objects.requireNonNull(obj, "Null values are not supported."), writer, mode);
+            writeRefObject(obj, writer, mode);
         }
 
         /** {@inheritDoc} */
@@ -275,7 +421,7 @@ public abstract class FieldAccessor {
 
         /** {@inheritDoc} */
         @Override public Object read(Tuple reader) {
-            return JavaSerializer.readRefValue(reader, colIdx, mode);
+            return readRefValue(reader, colIdx, mode);
         }
 
         /** {@inheritDoc} */
@@ -486,12 +632,12 @@ public abstract class FieldAccessor {
                 return;
             }
 
-            JavaSerializer.writeRefObject(val, writer, mode);
+            writeRefObject(val, writer, mode);
         }
 
         /** {@inheritDoc} */
         @Override public void read0(Object obj, Tuple reader) {
-            Object val = JavaSerializer.readRefValue(reader, colIdx, mode);
+            Object val = readRefValue(reader, colIdx, mode);
 
             varHandle.set(obj, val);
         }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
index 4bbb630..5e63e33 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
@@ -17,14 +17,11 @@
 
 package org.apache.ignite.internal.schema.marshaller.reflection;
 
-import java.util.BitSet;
-import java.util.UUID;
 import org.apache.ignite.internal.schema.ByteBufferTuple;
 import org.apache.ignite.internal.schema.Columns;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.Tuple;
 import org.apache.ignite.internal.schema.TupleAssembler;
-import org.apache.ignite.internal.schema.marshaller.BinaryMode;
 import org.apache.ignite.internal.schema.marshaller.SerializationException;
 import org.apache.ignite.internal.schema.marshaller.Serializer;
 
@@ -34,151 +31,6 @@ import static org.apache.ignite.internal.schema.marshaller.MarshallerUtil.getVal
  * Reflection based (de)serializer.
  */
 public class JavaSerializer implements Serializer {
-
-    /**
-     * Reads value object from tuple.
-     *
-     * @param reader Reader.
-     * @param colIdx Column index.
-     * @param mode Binary read mode.
-     * @return Read value object.
-     */
-    static Object readRefValue(Tuple reader, int colIdx, BinaryMode mode) {
-        assert reader != null;
-        assert colIdx >= 0;
-
-        Object val = null;
-
-        switch (mode) {
-            case BYTE:
-                val = reader.byteValueBoxed(colIdx);
-
-                break;
-
-            case SHORT:
-                val = reader.shortValueBoxed(colIdx);
-
-                break;
-
-            case INT:
-                val = reader.intValueBoxed(colIdx);
-
-                break;
-
-            case LONG:
-                val = reader.longValueBoxed(colIdx);
-
-                break;
-
-            case FLOAT:
-                val = reader.floatValueBoxed(colIdx);
-
-                break;
-
-            case DOUBLE:
-                val = reader.doubleValueBoxed(colIdx);
-
-                break;
-
-            case STRING:
-                val = reader.stringValue(colIdx);
-
-                break;
-
-            case UUID:
-                val = reader.uuidValue(colIdx);
-
-                break;
-
-            case BYTE_ARR:
-                val = reader.bytesValue(colIdx);
-
-                break;
-
-            case BITSET:
-                val = reader.bitmaskValue(colIdx);
-
-                break;
-
-            default:
-                assert false : "Invalid mode: " + mode;
-        }
-
-        return val;
-    }
-
-    /**
-     * Writes reference value to tuple.
-     *
-     * @param val Value object.
-     * @param writer Writer.
-     * @param mode Write binary mode.
-     */
-    static void writeRefObject(Object val, TupleAssembler writer, BinaryMode mode) {
-        assert writer != null;
-
-        if (val == null) {
-            writer.appendNull();
-
-            return;
-        }
-
-        switch (mode) {
-            case BYTE:
-                writer.appendByte((Byte)val);
-
-                break;
-
-            case SHORT:
-                writer.appendShort((Short)val);
-
-                break;
-
-            case INT:
-                writer.appendInt((Integer)val);
-
-                break;
-
-            case LONG:
-                writer.appendLong((Long)val);
-
-                break;
-
-            case FLOAT:
-                writer.appendFloat((Float)val);
-
-                break;
-
-            case DOUBLE:
-                writer.appendDouble((Double)val);
-
-                break;
-
-            case STRING:
-                writer.appendString((String)val);
-
-                break;
-
-            case UUID:
-                writer.appendUuid((UUID)val);
-
-                break;
-
-            case BYTE_ARR:
-                writer.appendBytes((byte[])val);
-
-                break;
-
-            case BITSET:
-                writer.appendBitmask((BitSet)val);
-
-                break;
-
-            default:
-                assert false : "Invalid mode: " + mode;
-        }
-    }
-
     /** Schema. */
     private final SchemaDescriptor schema;
 
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
deleted file mode 100644
index 74ea3a3..0000000
--- a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.util;
-
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import sun.misc.Unsafe;
-
-/**
- * Unsafe utility.
- */
-//TODO Move class to 'java-8' profile. Java9+ should use varhandles instead.
-public final class IgniteUnsafeUtils {
-    /** Unsafe. */
-    private static final Unsafe UNSAFE = unsafe();
-
-    /**
-     * @return Instance of Unsafe class.
-     */
-    private static Unsafe unsafe() {
-        try {
-            return Unsafe.getUnsafe();
-        }
-        catch (SecurityException ignored) {
-            try {
-                return AccessController.doPrivileged(
-                    new PrivilegedExceptionAction<Unsafe>() {
-                        @Override public Unsafe run() throws Exception {
-                            Field f = Unsafe.class.getDeclaredField("theUnsafe");
-
-                            f.setAccessible(true);
-
-                            return (Unsafe)f.get(null);
-                        }
-                    });
-            }
-            catch (PrivilegedActionException e) {
-                throw new RuntimeException("Could not initialize intrinsics.", e.getCause());
-            }
-        }
-    }
-
-    /**
-     * Returns object field offset.
-     *
-     * @param field Field.
-     * @return Object field offset.
-     */
-    public static long objectFieldOffset(Field field) {
-        return UNSAFE.objectFieldOffset(field);
-    }
-
-    /**
-     * Gets boolean value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Boolean value from object field.
-     */
-    public static boolean getBooleanField(Object obj, long fieldOff) {
-        return UNSAFE.getBoolean(obj, fieldOff);
-    }
-
-    /**
-     * Stores boolean value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putBooleanField(Object obj, long fieldOff, boolean val) {
-        UNSAFE.putBoolean(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets byte value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Byte value from object field.
-     */
-    public static byte getByteField(Object obj, long fieldOff) {
-        return UNSAFE.getByte(obj, fieldOff);
-    }
-
-    /**
-     * Stores byte value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putByteField(Object obj, long fieldOff, byte val) {
-        UNSAFE.putByte(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets short value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Short value from object field.
-     */
-    public static short getShortField(Object obj, long fieldOff) {
-        return UNSAFE.getShort(obj, fieldOff);
-    }
-
-    /**
-     * Stores short value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putShortField(Object obj, long fieldOff, short val) {
-        UNSAFE.putShort(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets char value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Char value from object field.
-     */
-    public static char getCharField(Object obj, long fieldOff) {
-        return UNSAFE.getChar(obj, fieldOff);
-    }
-
-    /**
-     * Stores char value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putCharField(Object obj, long fieldOff, char val) {
-        UNSAFE.putChar(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets integer value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Integer value from object field.
-     */
-    public static int getIntField(Object obj, long fieldOff) {
-        return UNSAFE.getInt(obj, fieldOff);
-    }
-
-    /**
-     * Stores integer value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putIntField(Object obj, long fieldOff, int val) {
-        UNSAFE.putInt(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets long value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Long value from object field.
-     */
-    public static long getLongField(Object obj, long fieldOff) {
-        return UNSAFE.getLong(obj, fieldOff);
-    }
-
-    /**
-     * Stores long value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putLongField(Object obj, long fieldOff, long val) {
-        UNSAFE.putLong(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets float value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Float value from object field.
-     */
-    public static float getFloatField(Object obj, long fieldOff) {
-        return UNSAFE.getFloat(obj, fieldOff);
-    }
-
-    /**
-     * Stores float value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putFloatField(Object obj, long fieldOff, float val) {
-        UNSAFE.putFloat(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets double value from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Double value from object field.
-     */
-    public static double getDoubleField(Object obj, long fieldOff) {
-        return UNSAFE.getDouble(obj, fieldOff);
-    }
-
-    /**
-     * Stores double value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putDoubleField(Object obj, long fieldOff, double val) {
-        UNSAFE.putDouble(obj, fieldOff, val);
-    }
-
-    /**
-     * Gets reference from object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @return Reference from object field.
-     */
-    public static Object getObjectField(Object obj, long fieldOff) {
-        return UNSAFE.getObject(obj, fieldOff);
-    }
-
-    /**
-     * Stores reference value into object field.
-     *
-     * @param obj Object.
-     * @param fieldOff Field offset.
-     * @param val Value.
-     */
-    public static void putObjectField(Object obj, long fieldOff, Object val) {
-        UNSAFE.putObject(obj, fieldOff, val);
-    }
-
-    /**
-     * Stub.
-     */
-    private IgniteUnsafeUtils() {
-    }
-}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
index f574dec..1896573 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
@@ -27,7 +27,7 @@ import javax.lang.model.element.Modifier;
 import org.apache.ignite.internal.schema.Column;
 import org.apache.ignite.internal.schema.Columns;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
-import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.CompilerUtils;
 import org.apache.ignite.internal.schema.marshaller.Serializer;
 import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
 import org.apache.ignite.internal.util.Factory;
@@ -75,7 +75,7 @@ public class SerializerBenchmarkTest {
     public int fieldsCount;
 
     /** Serializer. */
-    @Param({"Janino", "Java"})
+    @Param({"Generated", "Java"})
     public String serializerName;
 
     /**
@@ -94,7 +94,7 @@ public class SerializerBenchmarkTest {
      */
     @Setup
     public void init() throws Exception {
-        Thread.currentThread().setContextClassLoader(MarshallerUtil.dynamicClassLoader());
+        Thread.currentThread().setContextClassLoader(CompilerUtils.dynamicClassLoader());
 
         long seed = System.currentTimeMillis();
 
@@ -110,7 +110,7 @@ public class SerializerBenchmarkTest {
         if ("Java".equals(serializerName))
             serializer = SerializerFactory.createJavaSerializerFactory().create(schema, Long.class, valClass);
         else
-            serializer = SerializerFactory.createJaninoSerializerFactory().create(schema, Long.class, valClass);
+            serializer = SerializerFactory.createGeneratedSerializerFactory().create(schema, Long.class, valClass);
     }
 
     /**
@@ -182,7 +182,7 @@ public class SerializerBenchmarkTest {
 
         final JavaFile javaFile = JavaFile.builder(packageName, classBuilder.build()).build();
 
-        final ClassLoader loader = MarshallerUtil.compileCode(javaFile);
+        final ClassLoader loader = CompilerUtils.compileCode(javaFile);
 
         try {
             return loader.loadClass(packageName + '.' + className);
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
index d10fdf7..7b41412 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
@@ -22,9 +22,7 @@ import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeSpec;
 import java.util.Arrays;
 import java.util.BitSet;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Random;
 import java.util.UUID;
@@ -276,7 +274,7 @@ public class JavaSerializerTest {
     public void testClassLoader(SerializerFactory factory) throws SerializationException {
         final ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
         try {
-            Thread.currentThread().setContextClassLoader(MarshallerUtil.dynamicClassLoader());
+            Thread.currentThread().setContextClassLoader(CompilerUtils.dynamicClassLoader());
 
             Column[] keyCols = new Column[] {
                 new Column("key", LONG, false)
@@ -395,7 +393,7 @@ public class JavaSerializerTest {
         final JavaFile javaFile = JavaFile.builder(packageName, classBuilder.build()).build();
 
         try {
-            return MarshallerUtil.compileCode(javaFile).loadClass(packageName + '.' + className);
+            return CompilerUtils.compileCode(javaFile).loadClass(packageName + '.' + className);
         }
         catch (Exception ex) {
             throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);