Instrumentación para contar cada declaración en una función Scala
Tengo un simple Scala función en la que quiero aumentar una variable de clase cada vez que se ejecuta una declaración.
class C {
var cnt: Int: 0
def fun(): Unit = {
var a: Int = 0
var b: Int = -10
var sum: Int = 0
sum = a + b
return sum
}
}
Quiero que la función sea así:
class C {
var cnt: Int: 0
def fun(): Unit = {
var a: Int = 0
cnt = cnt + 1
var b: Int = -10
cnt = cnt + 1
var sum: Int = 0
cnt = cnt + 1
sum = a + b
cnt = cnt + 1
return sum
}
}
Pero hacerlo a nivel de software hace que el código sea desordenado. Can Uso la manipulación de códigos bytecode para implementar esto o debería intervenir en otros niveles de compilación?
Pregunta hecha hace 3 años, 5 meses, 6 días - Por coderconqueror
3 Respuestas:
-
No estoy seguro de quién agregó la etiqueta Byteman a esta pregunta pero Byteman no va a ser capaz de ayudarte con esto. Como un cartel anterior dijo que este no es realmente un problema que se puede resolver utilizando la manipulación de bytecode. El código Bytecode tiene un vínculo bastante opaco con la línea de origen y los límites de declaración de origen y a menudo no es posible derivar de cualquier tipo de límite de las instrucciones codificadas en el código bytecode.
Un traductor fuente a fuente basado en un analizador de idiomas sería una opción mucho mejor.
Respondida el Dec 16, 2020 a las 16:27 - por cybermage
Votos positivos: 0 | Votos negativos: 0 -
Lo que estás buscando se conoce comúnmente como instrumentación de código. En Scala, es posible realizar instrumentación de código a nivel de bytecode utilizando bibliotecas como ASM (objeto Scala de ASM, Kotlin de ASM) que te permiten modificar el bytecode de una clase en tiempo de compilación.
Sin embargo, este enfoque puede resultar complejo y difícil de mantener, especialmente para tareas simples como contar el número de declaraciones en una función. En su lugar, te sugiero utilizar técnicas más simples para lograr el mismo objetivo sin necesidad de manipulación de bytecode.
Por ejemplo, podrías refactorizar tu función
fun
de la siguiente manera:scala class C { var cnt: Int = 0 def incrementCount(): Unit = { cnt = cnt + 1 } def fun(): Int = { var a: Int = 0 incrementCount() var b: Int = -10 incrementCount() var sum: Int = 0 incrementCount() sum = a + b incrementCount() sum } }
Al separar la lógica de aumento de contador en un método aparte (
incrementCount
), tu código se mantiene más limpio y fácil de entender. Además, esta aproximación es más flexible y legible que la manipulación de bytecode.Por lo tanto, te recomendaría optar por una solución más sencilla y directa en este caso y evitar la manipulación de bytecode a menos que sea absolutamente necesario y no exista una alternativa más simple.
Respondida el Dec 17, 2020 a las 16:31 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Instrumentación de bytecode
La instrumentación de bytecode es una técnica que se puede utilizar para modificar el bytecode de una función en tiempo de ejecución. En este caso, puedes usarla para insertar instrucciones que incrementen el contador
cnt
cada vez que se ejecuta una declaración.Implementación usando ASM
Puedes utilizar la biblioteca ASM de Java para manipular el bytecode. Aquí tienes un ejemplo de cómo puedes instrumentar la función
fun
para incrementarcnt
:java import org.objectweb.asm.; public class BytecodeInstrumentation { public static void main(String[] args) { ClassReader cr = new ClassReader("C"); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(Opcodes.ASM5, mv) { @Override public void visitInsn(int opcode) { if (opcode == Opcodes.RETURN) { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, "C", "cnt", "I"); mv.visitInsn(Opcodes.ICONST_1); mv.visitInsn(Opcodes.IADD); mv.visitFieldInsn(Opcodes.PUTFIELD, "C", "cnt", "I"); } super.visitInsn(opcode); } }; } }; cr.accept(cv, ClassReader.SKIP_DEBUG); byte[] instrumentedBytecode = cw.toByteArray(); // Crea una nueva clase a partir del bytecode instrumentado Class> instrumentedClass = new ClassLoader() { @Override public Class> defineClass(String name, byte[] b, int off, int len) { return super.defineClass(name, b, off, len); } }.defineClass("C", instrumentedBytecode, 0, instrumentedBytecode.length); // Crea una instancia de la clase instrumentada C instance = (C) instrumentedClass.newInstance(); // Llama a la función instrumentada int result = instance.fun(); // Imprime el valor del contador System.out.println("Contador: " + instance.cnt); } }
Ventajas y desventajas de la instrumentación de bytecode
Ventajas:
Permite modificar el comportamiento del código en tiempo de ejecución. No requiere cambios en el código fuente original.
Desventajas:
Puede ser complejo y propenso a errores. Puede afectar al rendimiento del programa.
Otras opciones
Además de la instrumentación de bytecode, también puedes considerar las siguientes opciones:
Generación de código: Utiliza una herramienta de generación de código para generar código que incremente el contador. Compilación de aspectos: Utiliza un marco de aspectos, como AspectJ, para interceptar llamadas a métodos y aumentar el contador. Herramientas de perfilado: Utiliza herramientas de perfilado, como YourKit, para recopilar información sobre el número de declaraciones ejecutadas.
La mejor opción para ti dependerá de tus requisitos específicos y de los recursos disponibles.
Respondida el Dec 17, 2020 a las 16:38 - por Gemini
Votos positivos: 0 | Votos negativos: 0