January 19, 2021, 05:43:39 AM
Welcome,
Guest
. Please
login
or
register
.
Did you miss your
activation email
?
1 Hour
1 Day
1 Week
1 Month
Forever
Login with username, password and session length
Home
Forum
Help
Search
Gallery
Login
Register
SyntaxBomb - Indie Coders
»
Languages & Coding
»
Blitz Code Archives
»
Miscellaneous
»
[bmx] Replace Method at Runtime by N [ 1+ years ago ]
« previous
next »
Print
Pages: [
1
]
Go Down
Author
Topic: [bmx] Replace Method at Runtime by N [ 1+ years ago ] (Read 498 times)
BlitzBot
Jr. Member
Posts: 1
[bmx] Replace Method at Runtime by N [ 1+ years ago ]
«
on:
June 29, 2017, 12:28:41 AM »
Title :
Replace Method at Runtime
Author :
N
Posted :
1+ years ago
Description :
Ever wanted to replace a method at runtime? No? Didn't think so. At any rate, I got bored and decided to see if I could do this, and it turns out I can.
For a simple explanation, this is how it works: every Blitz object has an is-a pointer, or a pointer to the class it is. This is standard fare for a lot of languages. Inside the class are the methods, the superclass, and debugging info. Inside the debugging info is information about the class, essentially a list of methods, fields, metadata, and other bits of information pertaining to the class. This is the important bit. Using the debugging information, you can find out the location of the method (and, consequently, get a pointer to the method if you want [hint: you have to pass the Self object too if you're going to call it]) and you'll replace the old method's address with the new one.
Now, this probably isn't safe, and it probably isn't something you should be doing, but it's still neat and I figured I'd share it.
I've also got bits of code to create and load classes at runtime (that is, I'm creating new classes, not creating instances of classes), but that's still buggy and experimental.
Code :
Code: BlitzMax
Strict
Global
SCOPE_NAME:
String
[
]
=
[
"NULL"
,
"FUNCTION"
,
"USERTYPE"
,
"LOCALBLOCK"
]
Const
SCOPE_FUNCTION=
1
Const
SCOPE_USERTYPE=
2
Const
SCOPE_LOCALBLOCK=
3
Global
DECL_NAME:
String
[
]
=
[
"END"
,
"CONST"
,
"LOCAL"
,
"FIELD"
,
"GLOBAL"
,
"VARPARAM"
,
"TYPEMETHOD"
,
"TYPEFUNCTION"
,
"NULL"
]
Const
DECL_END =
0
Const
DECL_CONST =
1
Const
DECL_LOCAL =
2
Const
DECL_FIELD =
3
Const
DECL_GLOBAL =
4
Const
DECL_VARPARAM =
5
Const
DECL_TYPEMETHOD =
6
Const
DECL_TYPEFUNCTION =
7
'#region Testing
Type
DebugScope
Field
_class:
Int
Field
kind%
Field
name$
Field
decls:
TList
Method
New
(
)
kind =
0
name =
""
decls =
New
TList
End
Method
Method
Spit
(
)
Print
"Kind: "
+SCOPE_NAME
[
kind
]
Print
"Name: "
+name
Print
"Decls {"
For
Local
i:DebugDecl =
EachIn
decls
i.Spit
(
)
; Print
""
Next
Print
"}"
End
Method
Function
ForClass:DebugScope
(
cp@
Ptr
)
If
cp =
Null
Then
Return
Null
EndIf
Local
scope:DebugScope =
New
DebugScope
scope._class =
Int
cp
Local
p%
Ptr
=
Int
Ptr
cp
p =
Int
Ptr
p
[
2
]
scope.kind = p
[
0
]
scope.name =
String
.FromCString
(
Byte
Ptr
p
[
1
]
)
p = p +
2
While
p
[
0
]
Local
decl:DebugDecl =
New
DebugDecl
decl.ref = p
decl.kind = p
[
0
]
decl.name =
String
.FromCString
(
Byte
Ptr
p
[
1
]
)
decl.tag =
String
.FromCString
(
Byte
Ptr
p
[
2
]
)
decl.opaque = p
[
3
]
scope.decls.AddLast
(
decl
)
p :+
4
Wend
Return
scope
End
Function
Function
ForName:DebugScope
(
_type$
)
Local
typeid:TTypeId = TTypeId.ForName
(
_type
)
If
typeid =
Null
Then
Return
Null
EndIf
Return
DebugScope.ForClass
(
Byte
Ptr
typeid._class
)
End
Function
Method
DeclForName:DebugDecl
(
declname$, declkind%
)
For
Local
i:DebugDecl =
EachIn
decls
If
i.kind = declkind
And
i.name = declname
Return
i
EndIf
Next
Return
Null
End
Method
End
Type
Type
DebugDecl
Field
ref@
Ptr
Field
kind%
Field
name$
Field
tag$
Field
opaque%
Method
New
(
)
kind =
8
opaque =
0
name =
""
tag =
""
End
Method
Method
Spit
(
)
Print
"Kind: "
+DECL_NAME
[
kind
]
Print
"Name: "
+name
Print
"Tag: "
+tag
Select
kind
Case
DECL_FIELD
Print
"Index: "
+opaque
Default
Print
"Opaque: "
+opaque
End
Select
End
Method
End
Type
'#endregion
Type
Foobar
Field
_name$
Method
setName
(
n$
)
_name = n
End
Method
Method
ToString$
(
)
Return
"Normal"
End
Method
End
Type
Function
ReplaceMethod@
Ptr
(
_method:
String
, inClass:
String
, with@
Ptr
, searchSuperTypes:
Int
=
False
)
Local
result@
Ptr
=
Null
Local
scope:DebugScope = DebugScope.ForName
(
inClass
)
Local
class:
Int
Ptr
=
Int
Ptr
scope._class
While
scope
And
class
Local
decl:DebugDecl = scope.DeclForName
(
_method, DECL_TYPEMETHOD
)
If
decl =
Null
And
searchSuperTypes
Then
class =
Int
Ptr
class
[
0
]
scope = DebugScope.ForClass
(
class
)
Continue
ElseIf
decl =
Null
scope =
Null
class =
Null
Exit
EndIf
Local
mp@
Ptr
Ptr
=
Byte
Ptr
Ptr
(
Byte
Ptr
(
class
)
+decl.opaque
)
result = mp
[
0
]
mp
[
0
]
= with
scope =
Null
class =
Null
Wend
Return
result
End
Function
Local
foo:Foobar =
New
Foobar
Local
scope:DebugScope = DebugScope.ForName
(
"Foobar"
)
scope.Spit
(
)
Local
decl:DebugDecl = scope.DeclForName
(
"ToString"
, DECL_TYPEMETHOD
)
Print foo.ToString
(
)
foo.setName
(
"razzledazzlerootbeer"
)
Local
oldMethod:
String
(
obj:
Object
)
oldMethod = ReplaceMethod
(
"ToString"
,
"Foobar"
, newToString,
False
)
Print
"New method: "
+foo.ToString
(
)
Print
"Old method: "
+oldMethod
(
foo
)
Function
newToString:
String
(
_self:Foobar
)
Return
"Foo._name = "
+_self._name
End
Function
Comments :
GW(Posted 1+ years ago)
Very Cool!
markcw(Posted 1+ years ago)
Can't you do this kind of thing with reflection?
plash(Posted 1+ years ago)
Awesome. Does the method have to be built with a parameter for itself or can you sneak that in in the pointer or something (for calling it like a pointer)?
N(Posted 1+ years ago)
markcw: You can't use reflection to alter a class, which is what I'm doing.Plash: It has to have a parameter for itself, as I do not know how you could slip the object into the call without wrapping it (see TMethod).
N(Posted 1+ years ago)
I added a ReplaceMethod function that should make it relatively easier to understand the exact process of replacing methods.It's also worth noting that certain methods
cannot
be replaced using the debugging info- specifically those in Object, String, and the array types. Those can be replaced, but you will have to know the offsets of the specific methods ahead of time, rather than using the debugging info to find them.Oddly enough, the New/Delete methods can be replaced, but I wouldn't recommend attempting this at all. These methods are the main reason why I'm having trouble inserting new classes into the application at runtime, since there's no real explanation of how to write the constructor in its entirety.
Azathoth(Posted 1+ years ago)
Interesting. Does it have to be run in debug mode?Edit: Is it possible to change the method of a single instance rather than a class?
N(Posted 1+ years ago)
I don't believe so, and no.
N(Posted 1+ years ago)
This is sort of old, but I was revisiting the idea, and this..<div class="quote"> Edit: Is it possible to change the method of a single instance rather than a class? </div>The answer is yes.You'll have to duplicate the object's class, modify the duplicate's methods, and change the instance's isa pointer. Downside is you'll have to modify bbObjectFree such that it deletes the duplicated class once the object is disposed of.Unfortunately, I have no code to show for this right now, but it should be easy enough to do since you're just copying the existing class.
Logged
Print
Pages: [
1
]
Go Up
« previous
next »
SyntaxBomb - Indie Coders
»
Languages & Coding
»
Blitz Code Archives
»
Miscellaneous
»
[bmx] Replace Method at Runtime by N [ 1+ years ago ]
SimplePortal 2.3.6 © 2008-2014, SimplePortal