Herramientas de usuario

Herramientas del sitio


errores_comunes_con_beanshell

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
errores_comunes_con_beanshell [2010/12/26 23:07]
al-khwarizmi
errores_comunes_con_beanshell [2011/09/04 17:27] (actual)
al-khwarizmi
Línea 1: Línea 1:
 ==== Errores comunes con BeanShell ==== ==== Errores comunes con BeanShell ====
 +
 +=== Mensajes de error ===
  
 Con el material visto en las secciones anteriores, puedes experimentar creando aventuras básicas que utilicen código BeanShell. Como nadie es perfecto, esto seguramente te llevará a cometer algún error. Los errores en el código BeanShell suelen aparecer en forma de errores de sintaxis o bien de excepciones no capturadas, que se muestran en las partidas como un texto largo en rojo. He aquí un ejemplo de un error de sintaxis: Con el material visto en las secciones anteriores, puedes experimentar creando aventuras básicas que utilicen código BeanShell. Como nadie es perfecto, esto seguramente te llevará a cometer algún error. Los errores en el código BeanShell suelen aparecer en forma de errores de sintaxis o bien de excepciones no capturadas, que se muestran en las partidas como un texto largo en rojo. He aquí un ejemplo de un error de sintaxis:
Línea 70: Línea 72:
 La primera línea de esta parte nos vuelve a repetir que el error estaba en el método ''​onInit()''​ y nos dice en qué línea empieza el método. A continuación,​ viene un volcado de la pila en el momento del error. Esto normalmente no resultará muy útil, al menos para un uso básico de AGE. Pero si alguna vez se produce un error que no sea culpa vuestra sino de algún bug interno de AGE (esperemos que no; pero nadie es perfecto :)) seguramente el autor de AGE os pida que le mandéis esta información de la pila para localizar el fallo interno. La primera línea de esta parte nos vuelve a repetir que el error estaba en el método ''​onInit()''​ y nos dice en qué línea empieza el método. A continuación,​ viene un volcado de la pila en el momento del error. Esto normalmente no resultará muy útil, al menos para un uso básico de AGE. Pero si alguna vez se produce un error que no sea culpa vuestra sino de algún bug interno de AGE (esperemos que no; pero nadie es perfecto :)) seguramente el autor de AGE os pida que le mandéis esta información de la pila para localizar el fallo interno.
  
 +Otras veces, en lugar de errores de sintaxis, nos podemos encontrar excepciones,​ como ésta:
 +
 +<​code>​bsh.TargetError found at world'​s parseCommand,​ command was mirar, error was Sourced file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​ : at Line: 6 : in file: inline evaluation of: ``/*Método de análisis sintáctico de la entrada*/ void parseCommand( Mobile aCreat . . . ''​ : it .getWeight ( ) 
 +
 +Called from method: parseCommand : at Line: 1 : in file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​ : parseCommand ( arg0 , arg1 , arg2 ) 
 +Target exception: java.lang.NullPointerException:​ Null Pointer in Method Invocation
 +**
 +Error: java.lang.NullPointerException:​ Null Pointer in Method Invocation
 +Location: inline evaluation of: ``/*Método de análisis sintáctico de la entrada*/ void parseCommand( Mobile aCreat . . . ''​
 +Line: 6
 +Offending text: it .getWeight ( ) 
 +Message: Sourced file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​
 +Detailed trace: Sourced file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​ : at Line: 6 : in file: inline evaluation of: ``/*Método de análisis sintáctico de la entrada*/ void parseCommand( Mobile aCreat . . . ''​ : it .getWeight ( ) 
 +
 +Called from method: parseCommand : at Line: 1 : in file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​ : parseCommand ( arg0 , arg1 , arg2 ) 
 +Target exception: java.lang.NullPointerException:​ Null Pointer in Method Invocation
 +
 + at bsh.UtilTargetError.toEvalError(Unknown Source)
 + at bsh.UtilEvalError.toEvalError(Unknown Source)
 + at bsh.BSHMethodInvocation.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHBlock.evalBlock(Unknown Source)
 + at bsh.BSHBlock.eval(Unknown Source)
 + at bsh.BshMethod.invokeImpl(Unknown Source)
 + at bsh.BshMethod.invoke(Unknown Source)
 + at bsh.BshMethod.invoke(Unknown Source)
 + at bsh.Name.invokeLocalMethod(Unknown Source)
 + at bsh.Name.invokeMethod(Unknown Source)
 + at bsh.BSHMethodInvocation.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at eu.irreality.age.ObjectCode.run(ObjectCode.java:​348)
 + at eu.irreality.age.World.execCode(World.java:​2050)
 + at eu.irreality.age.Player.execCommand(Player.java:​867)
 + at eu.irreality.age.Player.execCommand(Player.java:​578)
 + at eu.irreality.age.Player.characterChangeState(Player.java:​2330)
 + at eu.irreality.age.Player.changeState(Player.java:​2167)
 + at eu.irreality.age.Entity.update(Entity.java:​308)
 + at eu.irreality.age.Player.update(Player.java:​252)
 + at eu.irreality.age.Entity.update(Entity.java:​94)
 + at eu.irreality.age.World.update(World.java:​2582)
 + at eu.irreality.age.GameEngineThread.run(GameEngineThread.java:​294)
 +
 +Target report: java.lang.NullPointerException:​ Null Pointer in Method Invocation
 + at bsh.Name.invokeMethod(Unknown Source)
 + at bsh.BSHMethodInvocation.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHBlock.evalBlock(Unknown Source)
 + at bsh.BSHBlock.eval(Unknown Source)
 + at bsh.BshMethod.invokeImpl(Unknown Source)
 + at bsh.BshMethod.invoke(Unknown Source)
 + at bsh.BshMethod.invoke(Unknown Source)
 + at bsh.Name.invokeLocalMethod(Unknown Source)
 + at bsh.Name.invokeMethod(Unknown Source)
 + at bsh.BSHMethodInvocation.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.BSHPrimaryExpression.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at bsh.Interpreter.eval(Unknown Source)
 + at eu.irreality.age.ObjectCode.run(ObjectCode.java:​348)
 + at eu.irreality.age.World.execCode(World.java:​2050)
 + at eu.irreality.age.Player.execCommand(Player.java:​867)
 + at eu.irreality.age.Player.execCommand(Player.java:​578)
 + at eu.irreality.age.Player.characterChangeState(Player.java:​2330)
 + at eu.irreality.age.Player.changeState(Player.java:​2167)
 + at eu.irreality.age.Entity.update(Entity.java:​308)
 + at eu.irreality.age.Player.update(Player.java:​252)
 + at eu.irreality.age.Entity.update(Entity.java:​94)
 + at eu.irreality.age.World.update(World.java:​2582)
 + at eu.irreality.age.GameEngineThread.run(GameEngineThread.java:​294)
 +**</​code>​
 +
 +Como vemos, la estructura del error es parecida, aunque se incluye algo más de información de depuración. La primera parte realmente importante es:
 +
 +<​code>​bsh.TargetError found at world'​s parseCommand,​ command was mirar, error was Sourced file: inline evaluation of: ``parseCommand( arg0, arg1, arg2);''​ : at Line: 6 : in file: inline evaluation of: ``/*Método de análisis sintáctico de la entrada*/ void parseCommand( Mobile aCreat . . . ''​ : it .getWeight ( )</​code>​
 +
 +En primer lugar, se nos dice que se ha encontrado un ''​bsh.TargetError''​. Esto no es más que un nombre genérico para las excepciones,​ cuando nos aparezca quiere decir que el error no está en la sintaxis del código, sino en lo que hace.
 +
 +A continuación,​ se dice que el error está en el parseCommand del mundo, y que el comando introducido fue "​mirar"​. Después se aclara que el error estaba en la línea 6, que contenía el código ''​it .getWeight ( )''​.
 +
 +La otra parte importante es la que nos dice qué tipo de excepción es la que ha aparecido:
 +
 +<​code>​Target exception: java.lang.NullPointerException:​ Null Pointer in Method Invocation</​code>​
 +
 +Se nos dice que es una excepción de tipo ''​NullPointerException''​ y, más concretamente,​ el texto nos explica: "​puntero nulo en invocación a método"​. Esto nos da la pista de lo que ha pasado: que en el código ''​it.getWeight()''​ que tiene el error, la variable ''​it''​ tiene valor ''​null'',​ y no podemos acceder al peso de una variable nula (que no está guardando realmente ningún objeto).
 +
 +Como vemos, si los sabemos interpretar,​ los mensajes de error de BeanShell nos suelen conducir directamente al error que hemos cometido. A continuación veremos una recopilación de mensajes de error comunes y lo que significa cada uno.
 +
 +=== Tipos de error comunes ===
 +
 +Para quienes no se lleven bien con el inglés o no estén muy familiarizados con los mensajes que suelen dar los compiladores e intérpretes de lenguajes de programación,​ he aquí una recopilación de los mensajes de error más comunes que se pueden encontrar en BeanShell y qué significan. La inmensa mayoría de los errores que se cometen en BeanShell son de alguno de estos tipos, y se depuran muy fácilmente conociendo los mensajes:
 +
 +<​code>​Syntax error (...) Parse error at line 5, column 5.  Encountered:​ end</​code>​
 +
 +Error genérico de sintaxis: el analizador sintáctico de código BeanShell se ha encontrado algún elemento en el código que no esperaba. Esto puede suceder, por ejemplo, si nos olvidamos de poner un punto y coma, de cerrar una llave, un paréntesis,​ etc. 
 +
 +El mensaje nos detalla la línea y la columna en la que se ha encontrado el error en el código, así como el elemento inesperado que el analizador sintáctico se encontró en ese lugar (en este caso, la palabra end). Con respecto a la información sobre la columna del error, hay que tener en cuenta que si por algún motivo indentaras el código con tabuladores,​ puede no coincidir con el número de columna que muestra el editor de PUCK. Por defecto, el editor indenta con espacios y de este modo el número de columna es fiable.
 +
 +También hay que tener en cuenta que a veces los errores de sintaxis se detectan en un lugar un poco distinto de donde nosotros hemos cometido el error. Por ejemplo, si nos hemos olvidado de abrir un paréntesis,​ es posible que el analizador sintáctico no se dé cuenta en el sitio donde tendríamos que abrirlo, sino más adelante, cuando se encuentre el cierre de paréntesis que le correspondía y ha quedado huérfano. Por lo tanto, la información de fila y columna es una orientación que a veces "​acertará"​ dónde estaba el error, pero no podemos fiarnos ciegamente de ella.
 +
 +<​code>​Syntax error (...) Command not found: nombre()</​code>​
 +
 +Se ha intentado utilizar una función de BeanShell que no existe. Por ejemplo, si en lugar de equals(a,b) escribiéramos erróneamente equal(a,b); saltaría este error.
 +
 +<​code>​java.lang.NullPointerException:​ Null Pointer in Method Invocation</​code>​
 +
 +Se ha llamado a un método sobre una variable nula. Es decir, se ha hecho ''​variable.metodo()''​ cuando el valor de ''​variable''​ es ''​null''​. Éste es uno de los errores más comunes; pero también de los más fáciles de depurar, pues siempre significa esto, y no hay más que ir a la línea de código que se menciona y ver qué objeto aparece antes de un punto y es ''​null'',​ y ya está, arreglado.
 +
 +<​code>​java.lang.NullPointerException</​code>​ (pero sin lo de Null Pointer in Method Invocation)
 +
 +Este error se genera porque se ha llamado a algún método que por dentro ha invocado a su vez otro método sobre una variable ''​null''​. En la práctica, en una aventura de AGE esto casi siempre será porque hemos pasado un ''​null''​ como parámetro a una función o método que requiere parámetros no nulos. Por ejemplo, si hacemos algo como ''​String s = null; Item it = item(s);''​ se nos provocará este error porque hemos pasado el parámetro nulo a la función ''​item()''​.
 +
 +<​code>​Syntax error (...) Error in method invocation: ​
 +Method hasIteem( eu.irreality.age.Item ) not found in class'​eu.irreality.age.Player'</​code>​
 +
 +Este error se genera porque hemos invocado a un método que no existe. Nos puede pasar tanto porque nos hayamos equivocado en el nombre del método, como en sus parámetros (por ejemplo, un método que requiera un parámetro de un tipo, pero lo llamemos con un parámetro de otro tipo).
 +
 +El error es muy fácil de depurar porque el mensaje nos muestra exactamente (además de la línea en la que se ha producido) cuál es el nombre y los parámetros del método que hemos intentado llamar, y sobre qué clase. Por ejemplo, en este caso, la causa del error es que escribimos ''​hasIteem''​ en lugar de ''​hasItem''​. Por eso nos dice que no encontró el método ''​hasIteem( eu.irreality.age.Item )''​ en la clase ''​Player''​.
 +
 +<​code>​java.lang.ArrayIndexOutOfBoundsException</​code>​
 +
 +Esta excepción ocurre si hemos intentado acceder a una posición ilegal de un array. Por ejemplo, si tenemos un array de tamaño 7 e intentamos acceder a un índice mayor que seis, o menor que cero.
 +
 +<​code>​Error:​ java.lang.IndexOutOfBoundsException:​ Index: 3, Size: 3</​code>​
 +
 +Esta excepción es análoga a la anterior, pero para listas. Se nos informa del índice ilegal al que hemos intentado acceder (en este caso, ''​3''​) y el tamaño de la lista (también ''​3''​). Con esto, y la posición del error en el código, no deberíamos tener problema para depurar el fallo.
 +
 +=== Funcionalidad de depuración ===
 +
 +AGE incluye la siguiente funcionalidad para ayudar a localizar y corregir errores en las aventuras:
 +
 +== Impresión de información de depuración en los parseCommand ==
 +
 +Si ejecutamos el siguiente código:
 +
 +<code java>
 +Debug.setCodeDebugging(true);​
 +</​code>​
 +
 +los parámetros de todos los métodos parseCommand que se llamen desde nuestra aventura aparecerán impresos en la salida de error estándar (que se muestra por defecto en la consola en los scripts de AGE para Linux y Mac, o en un log en el script de Windows).
 +
 +== Debugger dinámico ==
 +
 +AGE dispone de un depurador o "​debugger"​ que permite evaluar expresiones BeanShell durante la ejecución de las aventuras. Se puede activar ejecutando el código
 +
 +<code java>
 +Debug.setEvalEnabled(true);​
 +</​code>​
 +
 +y una vez activado, podemos utilizar la orden especial "​eval"​ en la aventura para obtener el valor de una expresión BeanShell en cualquier momento del juego. Por ejemplo, podríamos hacer
 +
 +<​code>​
 +eval 1+1
 +eval get ( mobile("​jugador"​) , "​cansado"​ )
 +eval item("​puerta roja"​).isClosed()
 +eval mobile("​goblin"​).hasItem(item("​espada"​))
 +</​code>​
 +
 +y nos aparecerá por pantalla el valor de esas expresiones en ese momento.
 +
 +== Breakpoints ==
 +
 +Si queremos hacer una depuración de grano más fino, evaluando expresiones no sólo en los momentos en los que podemos introducir un comando sino en medio de la ejecución del código, podemos utilizar la funcionalidad de "​breakpoints"​ que proporciona AGE desde su versión 1.1.6b.
 +
 +Un breakpoint es un punto donde la ejecución de código (en este caso el código BeanShell) se pausa hasta que nosotros le indiquemos que continúe. Mientras la ejecución está pausada, podemos evaluar expresiones y ver qué valores toman las variables en ese punto de la ejecución.
 +
 +Para poner un breakpoint en nuestro código, sólo tenemos que ejecutar la función BeanShell
 +
 +<code java>​breakpoint();</​code>​
 +
 +O bien, si queremos darle un nombre al breakpoint para distinguirlo de otros,
 +
 +<code java>​breakpoint("​Nombre del breakpoint"​);</​code>​
 +
 +Cuando la ejecución del código llegue a ese punto, se nos mostrará una ventana donde podremos ver:
 +
 +  * El nombre del breakpoint en la barra de título (de este modo, si tenemos varios, podemos ver en cuál estamos parados en ese momento).
 +  * Un campo de texto donde podemos introducir una expresión o una sentencia o serie de sentencias BeanShell y evaluarlas como si se ejecutaran en ese punto del código, obteniendo debajo su valor. Nótese que si las sentencias cambian valores de variables o propiedades,​ estos cambios tendrán efecto en el código (es decir, si ponemos ''​i=1'',​ aparte de obtener el valor de esa expresión - que es 1 - también estaremos cambiando el valor de la variable en la ejecución).
 +  * Una lista de las variables locales que hay declaradas y sus valores. Nótese que, por un problema con BeanShell, en esta lista no se muestran los contadores declarados en la cabecera de los bucles ''​for''​. ​ Esto no debería ser un problema, ya que se pueden declarar dichos contadores como variables externas al bucle y entonces sí se mostrarán.
 +  * Un botón que nos permite continuar la ejecución, saliendo del breakpoint. ​
errores_comunes_con_beanshell.1293401251.txt.gz · Última modificación: 2010/12/26 23:07 por al-khwarizmi