thrifty icon indicating copy to clipboard operation
thrifty copied to clipboard

Reflective extensions code dump

Open ZacSweers opened this issue 6 years ago • 0 comments

Talked offline, here's a dump of reflective extensions we currently employ because 🙃

package com.uber.thriftyextensions

import com.microsoft.thrifty.schema.Field
import com.microsoft.thrifty.schema.Location
import com.microsoft.thrifty.schema.Requiredness
import com.microsoft.thrifty.schema.UserElement
import com.microsoft.thrifty.schema.UserType
import com.microsoft.thrifty.schema.parser.ConstValueElement
import com.microsoft.thrifty.schema.parser.FieldElement
import java.lang.reflect.Modifier
import java.util.UUID

/*
 * Unsafe reflection-based extensions to Thrifty's APIs. Necessary because there's no APIs available
 * for adjusting [FieldElement] APIs.
 */

/**
 * Creates a new copy of this [Field] with `required` [Field.required]ness reflectively set via its
 * internal [FieldElement] field.
 *
 * @receiver the source [Field] to use. A defensive copy will be made.
 * @return the new [Field] instance.
 */
fun Field.makeRequired(): Field {
  val newElement = (elementField.get(this) as FieldElement).copy(requiredness = Requiredness.REQUIRED)
  return toBuilder().build()
      .apply {
        elementField.set(this, newElement)
      }
}

/**
 * Creates a new copy of this [Field] with `optional` [Field.required]ness reflectively set via its
 * internal [FieldElement] field.
 *
 * @receiver the source [Field] to use. A defensive copy will be made.
 * @return the new [Field] instance.
 */
fun Field.makeOptional(): Field {
  val newElement = (elementField.get(this) as FieldElement).copy(requiredness = Requiredness.OPTIONAL)
  return toBuilder().build()
      .apply {
        elementField.set(this, newElement)
      }
}

/**
 * Creates a new copy of this [Field] with the specified [value] default value reflectively set via
 * its internal [FieldElement] field.
 *
 * @receiver the source [Field] to use. A defensive copy will be made.
 * @return the new [Field] instance.
 */
fun Field.withDefaultValue(value: ConstValueElement): Field {
  val newElement = (elementField.get(this) as FieldElement).copy(constValue = value)
  return toBuilder().build()
      .apply {
        elementField.set(this, newElement)
      }
}

/**
 * Creates a new copy of this [Field] with the specified [newId] reflectively set via its internal
 * [FieldElement] field.
 *
 * @param newId the new id to set.
 * @receiver the source [Field] to use. A defensive copy will be made.
 * @return the new [Field] instance.
 */
fun Field.newId(newId: Int): Field {
  val newElement = (elementField.get(this) as FieldElement).copy(fieldId = newId)
  return toBuilder().build()
      .apply {
        elementField.set(this, newElement)
      }
}

/**
 * Ensures [this] list of [Field]s are valid (such as ensuring they've got unique field IDs).
 *
 * @param ensuredLocation the [Location] of the element holding this list to ensure fields match it.
 * @receiver the source list to check. A defensive new copy will be made.
 * @return the new fields list with the new field appended to the end.
 */
fun List<Field>.asSafeFields(ensuredLocation: Location): List<Field> {
  if (distinctBy { it.id }.size == size) {
    // They're all distinct, just return them with locations ensured
    return map {
      it.toBuilder()
          .location(it.location.matchTo(ensuredLocation))
          .build()
    }
  }
  val safeFields = fold(emptyList<Field>()) { cur, next ->
    cur.safeAddField(next, ensuredLocation)
  }
  check(safeFields.size == size) {
    "Data lost!"
  }
  return safeFields
}

/**
 * Safely adds a new [Field] to this collection while ensuring that it does not conflict with the
 * existing field ids.
 *
 * @param newField the new [Field] to add.
 * @param ensuredLocation the [Location] of the element holding this list to ensure fields match it.
 * @receiver the source list to add this to. A defensive new copy will be made.
 * @return the new fields list with the new field appended to the end.
 */
fun List<Field>.safeAddField(newField: Field, ensuredLocation: Location): List<Field> {
  val defensiveCopy = newField.toBuilder()
      .location(newField.location.matchTo(ensuredLocation))
      .build()
  if (none { it.id == newField.id }) {
    return plus(defensiveCopy)
  }
  val finalField = map(Field::id).max()?.let { maxId ->
    defensiveCopy.newId(maxId + 1)
  } ?: defensiveCopy
  return plus(finalField)
}

private fun Location.matchTo(source: Location): Location {
  return if (base == source.base && path == source.path) {
    this
  } else {
    Location.get(source.base, source.path)
  }
}

/**
 * Creates a new copy of this [Field] with the specified [newUUID] reflectively set via its internal
 * [FieldElement] field.
 *
 * @param newUUID the new UUID to use. Defaults to a randomly generated one.
 * @receiver the source [Field] to use. A defensive copy will be made.
 * @return the new [Field] instance.
 */
fun <T : UserElement> T.newUuid(newUUID: UUID = UUID.randomUUID()): T {
  if (this is UserType) {
    UserType::class.java.getDeclaredField("mixin")
  } else {
    javaClass.getDeclaredField("mixin")
  }
      .apply {
        isAccessible = true
        val modifiersField = java.lang.reflect.Field::class.java
            .getDeclaredField("modifiers")
        modifiersField.isAccessible = true
        modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
      }
      .get(this)
      .let { mixin ->
        // UserElementMixin
        mixin.javaClass.getDeclaredField("uuid")
            .apply {
              isAccessible = true
              val modifiersField = java.lang.reflect.Field::class.java
                  .getDeclaredField("modifiers")
              modifiersField.isAccessible = true
              modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
            }
            .apply {
              val olduuid = uuid
              set(mixin, newUUID)
              check(olduuid != uuid) {
                "New UUID setting failed!"
              }
            }
      }
  return this
}

/**
 * Sets a new default value for a given [Field].
 *
 * @param const the new [ConstValueElement] to set
 * @return this same [Field] instance.
 */
fun Field.setDefaultValue(const: ConstValueElement): Field {
  val newElement = (elementField.get(this) as FieldElement).copy(constValue = const)
  elementField.set(this, newElement)
  return this
}

private val elementField = Field::class.java.getDeclaredField("element")
    .apply {
      isAccessible = true
      val modifiersField = java.lang.reflect.Field::class.java
          .getDeclaredField("modifiers")
      modifiersField.isAccessible = true
      modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
    }

ZacSweers avatar Feb 06 '19 11:02 ZacSweers