Quita, que tú no sabes;ya lo hago yoGeneración e inyección de código entiempo de compilaciónby @Alberto_S_H
MADRID · NOV 18-19 · 2016
MADRID · NOV 18-19 · 2016
¿De qué vamos a hablar?1)Análisis de código2)Generación de código nuevo3)Modificación de código existente
MADRID · NOV 18-19 · 2016
Motivación● Ciclo de vida humano
– Nacer
– Crecer
– Reproducirse
– Morir
● Ciclo de vida del código
– Es escrito
– Crece (se escribe más)
– ?
– Queda olvidado en un commit
MADRID · NOV 18-19 · 2016
Motivación
¿Y el código no podría “reproducirse” ygenerar código nuevo él solito?
MADRID · NOV 18-19 · 2016
Índice1)Análisis de código2)Generación de código nuevo3)Modificación de código existente
MADRID · NOV 18-19 · 2016
Proceso1. Reconocimiento de anotaciones presentes
2. Reconocimiento de factories de procesadores de anotaciones
3. Solicitud* de procesador
4. Ejecución del procesador
5. Si se han generado nuevos ficheros de código volver al paso 1
* Sólo si el procesador actúa sobre alguna de las anotaciones presentes en el código
MADRID · NOV 18-19 · 2016
¿Qué está pasando?1. Nuestra clase está anotada con @Analysis1
2. Hemos declarado nuestro processor (a veces también llamado compiler)
3. Nuestro compiler tiene un constructor por defecto así que puede ser instanciado
4. Se lanza el método process(…)
5. No se genera código nuevo => termina el procesado
MADRID · NOV 18-19 · 2016
¿Y eso cómo se usa?● Clases del paquete javax.lang.model.element
● Documentación completa enhttps://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/package-summary.html
MADRID · NOV 18-19 · 2016
Element● getEnclosedElements()
● getEnclosingElement()
● getKind()
● getModifiers()
● getSimpleName()
● asType()
● getAnnotation(Class<A> annotationType)
MADRID · NOV 18-19 · 2016
TypeElement● getInterfaces()
● getSuperClass()
● getTypeParameters()
● getQualifiedName()
● getSimpleName()
MADRID · NOV 18-19 · 2016
ExecutableElement● getParameters()
● getReturnType()
● getSimpleName()
● getThrownTypes()
● getTypeParameters()
MADRID · NOV 18-19 · 2016
Un poquito más...● Dagger 2
● Análisis estático de dependencias
● Recorre todas nuestras clases en busca de @Component, @Module e @Inject
● Grafo de dependencias en tiempo de compilación
● Fetén!
MADRID · NOV 18-19 · 2016
Nuestra versión de Dagger 2:Toothpick 1● Analizador de dependencias sencillito
● Buscar los constructores anotados con @Toothpick
● Para cada constructor imprimir por pantalla el nombre completo de sus dependencias
MADRID · NOV 18-19 · 2016
Push it to the limit!● Hacer documentación es aburriiiiiiidooooo
● ¿Quién mejor que nuestro código sabe qué hace?
MADRID · NOV 18-19 · 2016
OpenAPI● “The Open API Initiative (OAI) was created by a consortium of forward-looking industry experts
who recognize the immense value of standardizing on how REST APIs are described.”
● ¿Quién está detrás?
– Atlassian
– IBM
– Microsoft
– PayPal
– …
● https://openapis.org/
MADRID · NOV 18-19 · 2016
OpenAPIswagger: '2.0'
info:
title: Uber API
description: Move your app forward with the Uber API
version: "1.0.0"
# the domain of the service
host: api.uber.com
# array of all schemes that your API supports
schemes:
- https
# will be prefixed to all paths
basePath: /v1
produces:
- application/json
paths:
/products:
get:
summary: Product Types
description: |
The Products endpoint returns information about the *Uber* products
offered at a given location. The response includes the display name
and other details about each product, and lists the products in the
proper display order.
parameters:
- name: latitude
in: query
description: Latitude component of location.
required: true
type: number
format: double
- name: longitude
in: query
description: Longitude component of location.
required: true
type: number
format: double
tags:
- Products
responses:
200:
description: An array of products
schema:
type: array
items:
$ref: '#/definitions/Product'
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
MADRID · NOV 18-19 · 2016
Swagplash● Generador de documentación
● Utilidades para PlayFramework!
– Parseo de parámetros
– Chequeo de existencia de parámetros en el body
– Chequeo de autenticación
● https://github.com/AlbertoSH/Swagplash
MADRID · NOV 18-19 · 2016
Índice1)Análisis de código2)Generación de código nuevo3)Modificación de código existente
MADRID · NOV 18-19 · 2016
Generación● Realmente hay poco que decir…
– Pero más que hacer!
● La gente de Square es maravillosa
– https://github.com/square/javapoet
MADRID · NOV 18-19 · 2016
Generación1. Reconocimiento de anotaciones presentes
2. Reconocimiento de factories de procesadores de anotaciones
3. Solicitud* de procesador
4. Ejecución del procesador
5. Si se han generado nuevos ficheros de código volver al paso 1
* Sólo si el procesador actúa sobre alguna de las anotaciones presentes en el código
MADRID · NOV 18-19 · 2016
JavaPoet en acción!● Escribamos un programa que escriba el programa más emocionante de todos!
● Hello World!!!
MADRID · NOV 18-19 · 2016
TypeSpec● classBuilder(...), interfaceBuilder(...), enumBuilder(…), anonymousClassBuilder(…),
annotationBuilder(…)
● addJavadoc(…)
● addAnnotation(…)
● addModifiers(…)
● superclass(…)
● addSuperInterface(…)
● addField(…)
● addMethod(...)
MADRID · NOV 18-19 · 2016
MethodSpec● methodBuilder(…), constructorBuilder(),
overriding(...)
● addJavadoc(…)
● addAnnotation(…)
● addModifiers(…)
● returns(...)
● addParameter(…)
● addException(…)
● addCode(…)
● addStatement(...)
● beginControlFlow(...)
● endControlFlow()
MADRID · NOV 18-19 · 2016
FieldSpec● builder(...)
● addJavadoc(…)
● addAnnotation(…)
● addModifiers(…)
● initializer(...)
MADRID · NOV 18-19 · 2016
SimpleBuilder● Vamos a implementar un generador del patrón Builder
● Bueno, casi...
MADRID · NOV 18-19 · 2016
SimpleBuilder@Builder
public class Sample {
private String someString;
private int someInteger;
// getters...
}
MADRID · NOV 18-19 · 2016
SimpleBuilder@Generated("Builder")
public class SampleBuilder {
private String someString;
private int someInteger;
public SampleBuilder() {
}
public SampleBuilder withSomeString(final String someString) {
this.someString = someString;
return this;
}
public final String getSomeString() {
return someString;
}
public SampleBuilder withSomeInteger(final int someInteger) {
this.someInteger = someInteger;
return this;
}
public final int getSomeInteger() {
return someInteger;
}
public SampleBuilder fromPrototype(final Sample prototype) {
this.someString = prototype.getSomeString();
this.someInteger = prototype.getSomeInteger();
return this;
}
public final Sample build() {
return new Sample(this);
}
}
MADRID · NOV 18-19 · 2016
SimpleBuilder1) Generar clase pública en el mismo paquete
2) Anotar con Generated(“Builder”)
3) Añadir atributos
4) Añadir getters
5) Añadir métodos with...
6) Añadir método fromPrototype(…)Cuidado con los atributos privados!
7) Añadir build()
MADRID · NOV 18-19 · 2016
No funciona :S● Problema: falta el constructor
● Nuestro código nunca va a compilar sin la intervención del usuario
● Y no es lo que queremos
MADRID · NOV 18-19 · 2016
Problem?● Podemos generar tooooodos los ficheros (.java o no) que queramos
● No podemos modificar ficheros .java existentes
MADRID · NOV 18-19 · 2016
Juguemos a invocar demonios!1) Dada una clase existente Sample
1) Con atributos privados someString y anotherString
2) Con setters públicos
2) Escribir un programa que
1) Cree una instancia de Sample
2) Use los setters
3) Recupere los valores de someString y anotherString y los imprima por pantalla
MADRID · NOV 18-19 · 2016
La reflection está bien pero...● Peligrosa
● Lenta
● Usar con mucho cuidado y sólo cuando es necesario
● Nunca puede competir con código compilado
MADRID · NOV 18-19 · 2016
Índice1)Análisis de código2)Generación de código nuevo3)Modificación de código existente
MADRID · NOV 18-19 · 2016
Compiladores● Analizador léxico → separar código en tokens
● Analizador sintáctico → comprobación de la gramática
● Analizador semántico → comprobación de “significado”
● Generador de código intermedio → código independiente de máquina
● Optimizador de código → independiente de la máquina
● Generador de código final → código dependiente de la máquina
MADRID · NOV 18-19 · 2016
Solución● El compilador de Java crea una estructura en memoria con nuestro código
● Podemos modificar dicha estructura
● Si te atreves, claro…
● Hasta ahora hemos visto brujería
● Ahora vamos a ver magia de sangre
MADRID · NOV 18-19 · 2016
Solución● Inyectar nodos en el AST del compilador de Java
● Todas las clases que usaremos son del paquete com.sun.tools.javac
● No son de uso “cotidiano” → hay que añadirlas al proyecto
● Añadir el fichero $JAVA_HOME/lib/tools.jar a las dependencias
● Listos para navegar por el AST
● Patrón visitor
MADRID · NOV 18-19 · 2016
Trees + TreeTranslator● intance(ProcessingEnvironment env)
● getTree(…)
● visit...(..)
MADRID · NOV 18-19 · 2016
TreeMaker● intance(Context context)
● ClassDef(…)
● MethodDef(…)
● VarDef(…)
● Block(…)
● WhileLoop(…)
● ForLoop(…)
● ...
MADRID · NOV 18-19 · 2016
Juguemos a invocar demonios!1) Dada una clase existente Sample
1) Con atributos privados someString y anotherString
2) Con setters públicos
2) Escribir un programa que
1) Cree una instancia de Sample
2) Use los setters
3) Recupere los valores de someString y anotherString y los imprima por pantalla
MADRID · NOV 18-19 · 2016
Reflection vs. AST manipulation● Rapidez → AST manipulation
● Seguridad → AST manipulation
● Manejo de excepciones → AST manipulation
● Sencillez → Reflection
● Potencia → AST manipulation
MADRID · NOV 18-19 · 2016
Y ahora?● Añadir soporte a herencia
● Tener en cuenta clases abstractas
● Añadir soporte a genéricos
MADRID · NOV 18-19 · 2016
Push it to the limit!● Implementar la capa de persistencia en DB es aburriiiiiiidooooo
● La mayoría de veces queda reducido a escribir codecs para serializar/deserializar
● De verdad hace falta tener a un programador perdiendo el tiempo en eso???
MADRID · NOV 18-19 · 2016
Ego● Easy Mongo
● Generador de capa de persistencia para MongoDB
● Operaciones seguras de:
– Lectura
– Escritura
– Borrado
– Actualización
● En desarrollo (actualmente parado)
MADRID · NOV 18-19 · 2016
Reflexiones finales● Análisis → aburrido pero necesario
● Generación → :)
● Modificación de AST → última solución