Análisis estático: decir si dos funciones de Javascript son iguales

Estoy buscando una manera, utilizando el análisis estático de dos funciones de JavaScript, para saber si son iguales. Permítanme definir múltiples definiciones de "lo mismo".

Nivel 1: Las funciones son las mismas excepto por posibles espacios en blanco diferentes, por ejemplo, TABS, CR, LF y SPACES.

Nivel A2 Las funciones pueden tener diferentes espacios en blanco como el Nivel 1, pero también pueden tener diferentes nombres de variable.

Nivel A3 ?


Para el nivel uno, creo que podría eliminar todos los espacios en blanco (no literales, lo que puede ser difícil) de cada cadena que contiene las dos definiciones de función JS, y luego comparar las cadenas.

Para el nivel dos, creo que necesitaría usar algo como Analizador sintáctico de SpiderMonkey para generar dos árboles de análisis, y luego escribir un comparador que recorra los árboles y permita que las variables tengan nombres diferentes.


[Editar] Williham, hace un buen punto a continuación. Me refiero a idénticos. Ahora, estoy buscando algunas estrategias prácticas, particularmente con respecto al uso de árboles de análisis.

preguntado el 16 de mayo de 11 a las 19:05

Para el nivel uno, su esquema no funcionará todo el tiempo. Por ejemplo, 0011 es "idéntico" a 00011 pero la normalización de espacios en blanco no verá eso; lo mismo ocurre con todas las demás variantes de "ortografía" del mismo valor, ya sea carácter, número o cadena. Para JavaScript, también debe preocuparse por los puntos y comas "opcionales". -

3 Respuestas

Reeditar:

Para exponer mi sugerencia para determinar funciones idénticas, se puede sugerir el siguiente flujo:

Nivel 1: elimine cualquier espacio en blanco que no forme parte de un literal de cadena; insertar nuevas líneas después de cada {, ; y } y comparar. Si es igual; las funciones son idénticas, si no:

Nivel 2: Mueva todas las declaraciones y asignaciones de variables que no dependen del estado de otras variables definidas en el mismo alcance al inicio del alcance en el que están declaradas (o si no desea analizar realmente el JS; el comienzo del tirantes); y ordenarlos por longitud de línea; tratar todos los nombres de las variables como si tuvieran 4 caracteres y volver al orden alfabético ignorando los nombres de las variables en el caso de longitudes vinculadas. Reordene todas las colecciones en orden alfabético y cambie el nombre de todas las variables vSNN, donde v es literal, S es el número de llaves anidadas y NN es el orden en el que se encontró la variable.

Comparar; si es igual, las funciones son idénticas, si no:

Nivel 3: Reemplace todos los literales de cadena con "sNN", Donde " y s son literales, y NN es el orden en el que se encontró la cadena. Comparar; si es igual, las funciones son idénticas, si no:

Nivel 4: normalice los nombres de cualquier función que se sepa que es la misma utilizando el nombre de la función con la prioridad más alta de acuerdo con el orden alfabético (en el ejemplo siguiente, cualquier llamada a p_strlen() sería reemplazado por c_strlen(). Repita los pedidos según el nivel 1 si es necesario. Comparar; si es igual, las funciones son idénticas, si no; es casi seguro que las funciones no sean idénticas.


Respuesta original:

Creo que encontrará que quiere decir "idéntico", no "igual".

La diferencia, como encontrará, es fundamental:

Dos funciones son idéntico si, siguiendo alguna forma de normalización, (eliminar espacios en blanco no literales, renombrar y reordenar variables a un orden normalizado, reemplazar cadenas literales con marcadores de posición,…) se comparan con literalmente iguales.

Dos funciones son lo mismo si, cuando se les solicita un valor de entrada dado, dan el mismo valor de retorno. Considere, en el caso general, un lenguaje de programación que ha contado, cadenas terminadas en cero (cadenas híbridas Pascal / C, por así decirlo). Una función p_strlen(str) podría mirar el recuento de caracteres de la cadena y devolverlo. Una función c_strlen(str) podría contar el número de caracteres en la cadena y devolverlo.

Si bien estas funciones ciertamente no serán idénticas, serán las mismas: para cualquier valor de entrada dado (válido) darán el mismo valor.


Mi punto es:

Determinar si dos funciones son idénticas (lo que parece querer lograr) es un problema (moderadamente) trivial, hecho como lo describe.

Determinar si dos funciones son realmente iguales (lo que en realidad podría querer lograr) no es trivial; de hecho, es francamente difícil, probablemente relacionado con el Problema de detencióny no algo que se pueda hacer con análisis estático.


Edit: Por supuesto, las funciones que son idénticas también son las mismas; pero de una manera muy específica y rara vez útil para un análisis completo.

contestado el 17 de mayo de 11 a las 01:05

Su enfoque para el nivel 1 parece razonable.

Para el nivel 2, ¿qué tal si hacemos alguna sustitución de variables rudimentarias en cada función y luego nos acercamos al nivel 1? Comience por el principio y para cada declaración de variable que encuentre, cámbieles el nombre a var1, var2, ... varX.

Esto no ayuda si las funciones declaran variables en diferentes órdenes ... var i y var j pueden usarse de la misma manera en ambas funciones pero se declaran en diferentes órdenes. Entonces probablemente te quedes haciendo una comparación de árboles de análisis como mencionaste.

contestado el 16 de mayo de 11 a las 23:05

Ver mi empresa (diseños semánticos) Diferenciador inteligente herramienta. Esta familia de herramientas analiza el código fuente de acuerdo con la gramática detallada a nivel del compilador para el lenguaje de interés (en su caso, JavaScript), construye AST y luego compara los AST (que efectivamente ignora los espacios en blanco y los comentarios). Los valores literales están normalizados, por lo que no importa cómo se "deletreen"; 10E17 tiene el mismo valor normalizado que 1E18.

Si los dos árboles son iguales, le dirá "no hay diferencias". Si difieren por un cambio de nombre consistente de un identificador, la herramienta le indicará el cambio de nombre consistente y el bloque en el que ocurre. Otras diferencias se informan como elementos de lenguaje (identificador, declaración, bloque, clase, ...) inserciones, eliminaciones, copias o movimientos. El objetivo es informar el pequeño conjunto de deltas que explican plausiblemente las diferencias. Puede ver ejemplos para varios idiomas en el sitio web.

En la práctica, no se puede ir mucho más allá de esto; Para determinar si dos funciones calculan la misma respuesta, en principio, debe resolver el problema de detención. Es posible que pueda detectar dónde se pueden conmutar sin efecto dos elementos del lenguaje que son elementos de una lista conmutativa; estamos trabajando en esto. Es posible que pueda aplicar reescrituras de normalización para canonicalizar ciertas formas (por ejemplo, mapear todas las declaraciones múltiples en una secuencia de declaraciones únicas ordenadas léxicamente). Es posible que pueda convertir el código fuente en su conjunto equivalente de flujos de datos y hacer una comparación de isomorfismo gráfico (el aprendiz de programador del MIT en la década de 1980 propuso hacer esto, pero no creo que hayan llegado allí).

Hay más trabajo por hacer del que cabría esperar.

contestado el 17 de mayo de 11 a las 02:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.