Quita, que tú no sabes; ya lo hago yo. Generación e inyección de código en tiempo de...

58
Quita, que tú no sabes; ya lo hago yo Generación e inyección de código en tiempo de compilación by @Alberto_S_H MADRID · NOV 18-19 · 2016

Transcript of Quita, que tú no sabes; ya lo hago yo. Generación e inyección de código en tiempo de...

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

¿Quién soy?● Ingeniero informático● Lead developer● Me gusta jugar

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

Análisis● APT + anotaciones

– Java 1.6

● Gradle (simplicidad)

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

APT en acción!

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

PackageElement● getQualifiedName()

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

VariableElement● getConstantValue()

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

– Google

– 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

OpenAPI + Swagger UI

MADRID · NOV 18-19 · 2016

OpenAPI + Swagger UI

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

¿Y esto cómo se usa?● Bien documentado en su README.md

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

TypeName & ClassName● get(...)

● ClassName.bestGuess(String)

MADRID · NOV 18-19 · 2016

Y ahora...

Vamos a jugar!!!

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

Solución● Vamos a “jaquiar” el compilador de Java!

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

Names● fromString(...)

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

Builder

Acabemos el builder!

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

MADRID · NOV 18-19 · 2016

DudasPreguntas

ComentariosSugerencias

Insultos