The story begins with the debugging of a new feature in a Koltin project. The application throws an exception with the stack information (partial) as follows:

[http-nio-8080-exec-2] ERROR c.c.p.f.i.c.c.e.GlobalExceptionHandler - java.lang.IllegalArgumentException: Parameter specified as non-null is null: method ${className}.<init>, parameter ${fieldName}
Copy the code

Is a common null-safe exception that assigns a Null value to a null-safe variable. But the value variable is also from the null security variable, why does this happen? Let’s do it step by step.

Koltin’s air safety

Kotlin’s null-safety of class member variables is implemented at the compile level by adding null-checking to class constructors at class file compilation time

/ / the first parameter is the constructor of the incoming variable values, and the second parameter is a variable name Intrinsics. CheckParameterIsNotNull (name,"name"); 
Copy the code

The inspection method will be thrown when introduced to a Null value. Java lang. IllegalArgumentException exception, assure the members of the class attribute is empty.

Deserialization of Gson

Gson deserialization main process logic focused on ReflectiveTypeAdapterFactory. BoundField. Read () method

@Override public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }

      T instance = constructor.construct();

      try {
        in.beginObject();
        while (in.hasNext()) {
          String name = in.nextName();
          BoundField field = boundFields.get(name);
          if(field == null || ! field.deserialized) { in.skipValue(); }else {
            field.read(in, instance);
          }
        }
      } catch (IllegalStateException e) {
        throw new JsonSyntaxException(e);
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      }
      in.endObject();
      return instance;
}
Copy the code

The process is simple:

  • checkJsonIs returned if the input stream is emptyNull
  • Instantiates an object instance of a generic parameter typeinstance
  • parsingJsonString toinstanceAttribute assignment

The most important step is the second, instantiation of the object constructor.construct(). What object is constructor?

Constructor is an object that encapsulates an object’s constructor, and its generation logic is as follows:

public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
    final Type type = typeToken.getType();
    final Class<? super T> rawType = typeToken.getRawType();

    // first try an instance creator

    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
    if (typeCreator ! = null) {return new ObjectConstructor<T>() {
        @Override public T construct() {
          return typeCreator.createInstance(type); }}; } // Next try rawtype match for instance creators
    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> rawTypeCreator =
        (InstanceCreator<T>) instanceCreators.get(rawType);
    if(rawTypeCreator ! = null) {return new ObjectConstructor<T>() {
        @Override public T construct() {
          return rawTypeCreator.createInstance(type); }}; } ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);if(defaultConstructor ! = null) {return defaultConstructor;
    }

    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
    if(defaultImplementation ! = null) {return defaultImplementation;
    }

    // finally try unsafe
    return newUnsafeAllocator(type, rawType);
  }
Copy the code
  • If you have registeredInstanceCreator, returns the registeredInstanceCreator
  • If the class has no argument constructor, returns a call to the no-argument constructorInstanceCreator
  • If it is a collection class return newDefaultImplementationConstructor generatedInstanceCreator
  • Otherwise toUnsafeAllocator

UnsafeAllocator (UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, sun.misc.Unsafe, UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, sun.misc.Unsafe, UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, UnsafeAllocator, sun.misc.

Class instantiation & sun.misc.unsafe

In Java/Kotlin, object creation is common in the following ways:

  • newStatements, such asMyClass demo = new MyClass()
  • ClassThe object’snewInstance()Methods, such asMyClass demo = MyClass.class.newInstance()(The premise is that you must provide a constructor with no arguments.)
  • usingConstructorObject to create an object

There are many ways, but they all lead to the same result. At the JVM level, they all correspond to three important instructions. Take Java.lang.StringBuilder for example

NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
Copy the code

The corresponding logical table is different

  • Allocate the memory required by the object and return the memory address to the top of the stack
  • Copy the above memory address and push it to the top of the stack
  • Performs the creation of the object<init>methods

As you can see, object creation is not atomic, so there is also a special way to create objects, the Sun.misc.unsafe class. It creates objects in a similar way to radiation, but it does not execute the INVOKESPECIAL command. All member variables of the object created are null.

So back to the application exception, at run time Gson generated a “semi-finished” object by deserialization that did not match the type declaration, and the exception was checked by Koltin when it was passed to an empty security field.

conclusion

Kotlin’s air security does bring great convenience to developers, but it also brings with it the potential to give developers a false sense of security: Even though Kotlin is working in a jVM-based static language, addressing low-level operations like Sun.misc.Unsafe, null-checking can be broken easily. Even though such illegal objects exist, they bring real bugs to applications. Guard against these “illegal immigrants”.