Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflective extensions code dump #293

Open
ZacSweers opened this issue Feb 6, 2019 · 0 comments
Open

Reflective extensions code dump #293

ZacSweers opened this issue Feb 6, 2019 · 0 comments

Comments

@ZacSweers
Copy link
Contributor

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())
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant