Kotlin的空安全真的安全嗎?

故事從一個Koltin項目新功能的調試過程提及,應用拋出了一個異常,堆棧信息(局部)以下:java

[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}
複製代碼

是一個常見的空安全異常,向空安全的變量賦了Null 值。但值變量是也是來自空安全變量,爲何會出現這樣的狀況呢?接下來逐步分析。安全

Koltin 的空安全

Kotlin的在類成員變量的空安全是在編譯級別實現的,即在編譯成class文件的時候在類的構造函數添加了空值檢查bash

// 第一個參數是構造函數傳入的變量值,第二個參數是變量名
Intrinsics.checkParameterIsNotNull(name, "name"); 
複製代碼

這個檢查方法會在傳入Null值時拋出java.lang.IllegalArgumentException異常,以此保證類的成員屬性是空安全。ide

Gson的反序列化

Gson的反序列化的主流程邏輯集中在ReflectiveTypeAdapterFactory.BoundField.read()方法中函數

@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;
}
複製代碼

流程十分簡單:ui

  • 檢查Json的輸入流,爲空則返回Null
  • 實例化泛型參數類型的對象實例instance
  • 解析Json字符串給instance屬性賦值

最重要的是第二步,即對象的實例化過程constructor.construct()constructor是一個什麼對象呢?spa

constructor是一個封裝了對象構造方法的對象,它的生成邏輯以下:調試

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 raw type 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);
  }
複製代碼
  • 若是註冊過 InstanceCreator ,則返回註冊的 InstanceCreator
  • 若是類有無參構造函數,則返回調用無參構造函數的 InstanceCreator
  • 若是是集合類,則返回 newDefaultImplementationConstructor生成的 InstanceCreator
  • 不然交給 UnsafeAllocator

此次異常的分支條件調用的是UnsafeAllocator,其源碼就不進行具體解析,其工做原理就是包裝了sun.misc.Unsafe的方法來完成對象的實例化,這個sun.misc.Unsafe就是此次異常的「病根」。code

類的實例化 & sun.misc.Unsafe

Java/Kotlin中,對象的建立方式比較常見的是如下幾種:對象

  • new語句, 好比 MyClass demo = new MyClass()
  • Class對象的newInstance()方法,好比MyClass demo = MyClass.class.newInstance() (前提就是必須提供無參的構造函數)
  • 利用Constructor對象來建立對象

方式雖多,但異曲同工,在JVM層面,其對應都是三條重要指令,以java.lang.StringBuilder爲例

NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
複製代碼

其對應的邏輯分表別是

  • 分配對象所需內存並返回內存地址壓入棧頂
  • 複製一份上述內存地址並在壓入棧頂
  • 執行建立對象的<init>方法

能夠看到對象的建立過程不是原子性的,因此還存在一種特殊途徑來建立對象,即sun.misc.Unsafe類。
它建立對象的方法和放射相似,但它不會執行INVOKESPECIAL指令,建立的對象的全部成員變量都處於空值狀態。

那麼回到應用的異常狀況,在運行時Gson經過反序列化生成了一個與類型聲明不符的「半成品」對象,這個異常值的傳遞至一個空安全字段時,就受到了Koltin的檢查致使異常發生。

總結

Kotlin的空安全確實給開發人員帶了極大的便利,但也帶來了隱患,便可能會給開發人員帶來虛假的安全感:Kotlin終究仍是在基於JVM的靜態語言,面對相似sun.misc.Unsafe等相似的底層操做,空檢查能夠被輕易突破,且這種非法對象的存在是渾然不知的,可是卻給咱們的應用帶來真切的Bug,在面對空安全屬性上,各位同窗仍是須要多留個心眼,防範這些「非法移民」。

相關文章
相關標籤/搜索