FTP | CCD | Buscar | Trucos | Trabajo | Foros |
|
Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
|
Herramientas | Buscar en Tema | Desplegado |
|
#1
|
|||
|
|||
Gracias Al por el pedazo de documentación. Siempre había tenido la sospecha (aunque nunca la había confirmado) de que las instancias comparten la VMT y NO poseen una copia para sí.
Cita:
Cita:
Según lo que he visto, todos lo resuelven así. No lo he visto implementado de ninguna otra manera... ni se me ocurre como. Y sí... es un poco turbio, pero al menos a mí me funciona. Creo que debería funcionar en versiones anteriores también, pues se viene usando desde hace mucho. -------- Con respecto a _AbstractError, tienes absoluta razón! Apuntan allí y desde allí se fija si está asignado AbstractErrorProc y, si es así, la llama, sino, sigue con "_RunError", rutina famosa que muestra los cartelos "Runtime Error" y luego termina en la función Halt0 (que se encarga de terminar la aplicación). debo ser la falta de sueño, de ansiolíticos o el exceso de horas en la máquina! (o la suma de todo. jejejeje) Estuve investigando algo más... algo de eso ya me había cruzado. En en INITIALIZATION del SysUtils.pas se llama (entre otras cosas) a el procedure InitExceptions que se encarga de inicializar la variable AbstractErrorProc del System.pas (y así también de otros errores como ser assert). El manejador de errores de Abstracción es sencillo: procedure AbstractErrorHandler; begin raise EAbstractError.CreateRes(@SAbstractError); end; la asignación también pero lo que me llama la atención son unos comentarios que hay alrededor de tal asignación y el IFNDEF: {$IFNDEF PC_MAPPED_EXCEPTIONS} // We don't hook this under PC mapped exceptions, because // we have no idea what the parameters were to the procedure // in question. Hence we cannot hope to unwind the stack in // our handler. Since we just throw an exception from our // handler, that pretty much rules out using this without // exorbitant compiler support. If you do hook AbstractErrorProc, // you must make sure that you never throw an exception from // your handler if PC_MAPPED_EXCEPTIONS is defined. AbstractErrorProc := @AbstractErrorHandler; {$ENDIF} Supongo que PC_MAPPED_EXCEPTIONS se trata de algo heredado del kylix y su compatibilidad con linux... Cita:
A ver si comprendo: una entrada de una función virtual en una VMT, si esta es abtracta, apuntará a _AbstractError. Cada Método Abstracto tiene un dirección de memoria particular (corresponfiende al Offset dentro de un array de punteros a métodos, no?). La pregunta es por qué hay un elementos en la el array que no están implementados? (es decir, apuntan a _AbstractError)? Siguiendo con la experimentación, querer reemplazar el puntero de un método virtúal de la VMT de la suguiente manera: vmt^.UserDefinedVirtuals[x] := @TForm1.MyMethod; me di cuenta de que no se podía por algo... ARROJABA UNA EXCEPCION! Tras de mirar y mirar si estaba haciendo algo mal, caí en que los Administradores de Memoria (memory managers) de los sistemas operativos que trabajan con microprocesadores que operan en modo protegido, marcan a las páginas de memoria con atributos especiales haciendo que estas se puedan (o no) leer, escribir y/o ejecutar. Claro que todo esto depende del microprocesador y/o sistema operativo... en la actualidad creo que ya todos los microsprocesadores lo soportan y el windows lo viene soportando desde hace rato... no tengo idea. La cuestión es que implementé una función que se encarga de desproteger la vmt y otra que vuelve a protegerla, para dejarla todo como estaba... bueno, casi Otra duda surge de acá: Qué pasaría si el DEP esta activado? Nota: Data Execution Prevention, es algo que se agregó a partir del SP2 del WinXP. Claro que el micro lo tiene que soportar. pd: urgar va con H... lo puse en el título y está mal. Se podrá cambiar? pd2: Adjunto RttiUtils.pas |
#2
|
||||
|
||||
¡Hola Poyo!
Viendo este hilo de Aeff, me vino a la mente que podría existir una manera de sustituir un método virtual por otro método, para casos como ese, donde estando ya un vasto árbol de clases definido, la derivación de clases sería una solución poco efectiva (si el compilador soportara herencia insertada, otro gallo cantaría ). Entonces recordé que habías mencionado algo sobre sustituir entradas de la VMT: Cita:
Seguramente ya habrás hecho algo similar. Pero, como podrás apreciar en el ejemplo, el principal problema es que no todas las llamadas a métodos virtuales son late binding, es decir, aquellas donde el programa determina en tiempo de ejecución a qué rutina va a saltar revisando primero de qué clase es el objeto. ¿Cuáles llamadas a métodos virtuales son resueltas en tiempo de compilación (realizadas "estáticamente")? Particularmente las que usan la palabra reservada Inherited, y también las que se llevan a cabo mediante variables procedimentales. Entonces, creo que la utilidad práctica de sustituir una entrada VMT de método virtual sería aplicable, básicamente, a aquellos casos donde ya existen varias clases derivadas de la clase que implementa el método a sustituir, pero ninguna de esas clases descendientes ha redefinido dicho método, o, si alguna lo ha hecho, no usa Inherited o no nos interesa que para ella se ejecute el código original (y, claro está, no queremos o no podemos modificar el código fuente de la clase ancestro en cuestión). Cita:
El camino que seguirá el programa es distinto, dependiendo de si el enlace de llamada al método virtual es estático o tardío (late binding). Con Inherited y variables procedimentales, saltará a la dirección del método abstracto sin consultar la VMT; mientras que con una llamada tipo "Objeto.Método" sí leerá la entrada respectiva de la VMT. ¿Interesante no? En cuanto a la función GetVirtualMethodCount, aún no me queda claro en qué se basa ésta para determinar que ha encontrado el fin de la VMT. El código, aún con comentarios, me resulta un poco confuso. Insisto en que tiene que haber una especie de marca o dato indicativo seguro en el cual pueda basarse la función para saber dónde termina la lista de entradas de la VMT, puesto que el tamaño de cada VMT es variable y, que yo sepa, no hay ningún lugar donde esté señalado el tamaño que tiene. O bien una regla de almacenamiento que no estoy percibiendo... Saludos. Al González. |
#3
|
|||
|
|||
Esto en vez de divertido parece ser un dolor de cabeza... jajajaja
Cada vez que aclaramos, oscurece... y termina siendo más estático de lo que me imaginaba. Ahora entiendo algunas limitaciones de los ClassHelpers. Hasta allí llegan... Para implementar herencia insertada o cosas por el estilo creo que los muchachos de codegear deberían de replantearse una buena parte del asunto. un arduo trabajo aunque, lo más difícil es decidirse a comenzar, no? Una vez que estás en camino casi nunca resulta tan complicado como parececía antes de empezar. No, no había hecho la prueba de reemplazar un método de la VMT y luego llamarlo mediante "inherited"... daba por hecho que esta función iba a buscar la VMT del ClassParent y allí resolvía el puntero a la función, pero resultó no ser así. es una lástima porque se hubiesen podido hacer cosas interesantes. Es muy peciliar el uso que se hace de la VMT. Parece ser que al final de cuentas termina siendo sólo realmente útil para la persistencia y alguna que otra cosita más. Con respecto a las llamadas (donde simplemente se hace un Jump): ... jmp dword prt [$XXXXXXXX] mov eax, eax ... Se me hace que estas direcciones de memoria (que apuntan a las funciones en cuestión) son llenadas en runtime en el LoadPackage mediante GetProcAddress. Algo similar pasa con las variables globales cuando son compiladas para usarse dinámicamente. En mi caso había una "tabla" de que saltos a funciones en los cuales estaban todos estos juntos (con rtl en runtime): abstract error tobject.initinstance tobject.freeinstance tobject.free tobject.equals tobject.gethashcode tobject.tostring tobject.dispatch handlefinally startexe halt0 registermodule finalization Volviendo a GetVirtualMethodCount, a mí tampoco me queda del todo claro. :S Sí cómo es obtenido y eso, pero exactamente porqué. Sé que juegan con punteros. Toman el final de la vmt, luego recorren las entradas buscando las direcciones de los punteos, buscan la más alta, hacen la diferencia, dividen por 4 y les da. (más magia negra!) pero porqué esos punteros y no otros? A mi entender, se basan en información que no he encontrado o no está disponible (en el orden en que es asignada (alloc) la memoria de la tabla de métodos virtuales). Es decir, SABEN con seguridad que hay un puntero que se contiguo a la tabla de métodos virtuales. |
#4
|
||||
|
||||
Cita:
Tal parece que la estructura de la VMT finaliza con el ShortString que contiene el nombre de la clase, lo cual es bastante lógico. Es información intrínseca de la VMT, pero, como es de longitud variable, en el desplazamiento vmtClassName sólo guardan un puntero a ese ShortString, así la estructura inicial permanece uniforme para todas las VMTs y rematan estas estructuras con el propio nombre de la clase. Es una técnica bastante usual colocar al final de una estructura las partes que pueden ser de tamaño variable. No lo he probado, pero entonces con esto sí podríamos saber cuántas entradas de métodos virtuales y de métodos de interfaces tiene una VMT. Ahora la pregunta sería, ¿cómo distinguir unas de otras? Y algo más de considerar: la función GetVirtualMethodCount que enlacé parece ser de una versión más reciente que la mostrada inicialmente, y en ella el algoritmo es significativamente distinto. Habrá que hacer algunas pruebas adicionales en un rato libre... Espero te alivies de los dolores de cabeza, Poyo, tómalo con calma. Son simples bytes. Me entusiasma participar con los colegas en este tipo de disertaciones. Saludos. Al González. |
|
|
|