Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
S
SandHook
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
SandHook
Commits
3e4cf9aa
Commit
3e4cf9aa
authored
Feb 18, 2019
by
swift_gan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add call origin error detect
parent
955fa990
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
137 additions
and
15 deletions
+137
-15
SandHook.java
hooklib/src/main/java/com/swift/sandhook/SandHook.java
+35
-6
XposedCompat.java
...in/java/com/swift/sandhook/xposedcompat/XposedCompat.java
+3
-1
ErrorCatch.java
...com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java
+22
-0
HookerDexMaker.java
...swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java
+77
-8
No files found.
hooklib/src/main/java/com/swift/sandhook/SandHook.java
View file @
3e4cf9aa
...
@@ -118,10 +118,12 @@ public class SandHook {
...
@@ -118,10 +118,12 @@ public class SandHook {
hookResultCallBack
.
hookResult
(
res
>
0
,
entity
);
hookResultCallBack
.
hookResult
(
res
>
0
,
entity
);
}
}
if
(
res
<
=
0
)
if
(
res
<
0
)
{
throw
new
HookErrorException
(
"hook method <"
+
entity
.
target
.
toString
()
+
"> error in native!"
);
throw
new
HookErrorException
(
"hook method <"
+
entity
.
target
.
toString
()
+
"> error in native!"
);
}
globalHookEntityMap
.
put
(
entity
.
target
,
entity
);
globalHookEntityMap
.
put
(
entity
.
target
,
entity
);
if
(
entity
.
backup
!=
null
)
{
if
(
entity
.
backup
!=
null
)
{
globalBackupMap
.
put
(
entity
.
backup
,
entity
);
globalBackupMap
.
put
(
entity
.
backup
,
entity
);
}
}
...
@@ -129,6 +131,33 @@ public class SandHook {
...
@@ -129,6 +131,33 @@ public class SandHook {
Log
.
d
(
"SandHook"
,
"method <"
+
entity
.
target
.
toString
()
+
"> hook <"
+
(
res
==
HookMode
.
INLINE
?
"inline"
:
"replacement"
)
+
"> success!"
);
Log
.
d
(
"SandHook"
,
"method <"
+
entity
.
target
.
toString
()
+
"> hook <"
+
(
res
==
HookMode
.
INLINE
?
"inline"
:
"replacement"
)
+
"> success!"
);
}
}
public
static
Object
callOriginMethod
(
Member
originMethod
,
Object
thiz
,
Object
[]
args
)
throws
Throwable
{
HookWrapper
.
HookEntity
hookEntity
=
globalHookEntityMap
.
get
(
originMethod
);
if
(
hookEntity
==
null
||
hookEntity
.
backup
==
null
)
return
null
;
return
callOriginMethod
(
originMethod
,
hookEntity
.
backup
,
thiz
,
args
);
}
public
static
Object
callOriginMethod
(
Member
originMethod
,
Method
backupMethod
,
Object
thiz
,
Object
[]
args
)
throws
Throwable
{
backupMethod
.
setAccessible
(
true
);
if
(
args
==
null
)
{
args
=
new
Object
[
0
];
}
final
int
argsSize
=
args
.
length
;
if
(
Modifier
.
isStatic
(
originMethod
.
getModifiers
()))
{
ensureMethodDeclaringClass
(
originMethod
,
backupMethod
);
return
backupMethod
.
invoke
(
null
,
args
);
}
else
{
Object
[]
newArgs
=
new
Object
[
argsSize
+
1
];
newArgs
[
0
]
=
thiz
;
for
(
int
i
=
1
;
i
<
newArgs
.
length
;
i
++)
{
newArgs
[
i
]
=
args
[
i
-
1
];
}
ensureMethodDeclaringClass
(
originMethod
,
backupMethod
);
return
backupMethod
.
invoke
(
null
,
newArgs
);
}
}
public
static
void
ensureBackupDeclaringClass
(
Method
backupMethod
)
{
public
static
void
ensureBackupDeclaringClass
(
Method
backupMethod
)
{
if
(
backupMethod
==
null
)
if
(
backupMethod
==
null
)
return
;
return
;
...
@@ -138,13 +167,13 @@ public class SandHook {
...
@@ -138,13 +167,13 @@ public class SandHook {
ensureMethodDeclaringClass
(
hookEntity
.
target
,
backupMethod
);
ensureMethodDeclaringClass
(
hookEntity
.
target
,
backupMethod
);
}
}
public
static
void
ensureBackupDelaringClassByOrigin
(
Member
originMethod
)
{
public
static
boolean
ensureBackupDelaringClassByOrigin
(
Member
originMethod
)
{
if
(
originMethod
==
null
)
if
(
originMethod
==
null
)
return
;
return
false
;
HookWrapper
.
HookEntity
hookEntity
=
globalHookEntityMap
.
get
(
originMethod
);
HookWrapper
.
HookEntity
hookEntity
=
globalHookEntityMap
.
get
(
originMethod
);
if
(
hookEntity
==
null
||
hookEntity
.
backup
==
null
)
if
(
hookEntity
==
null
||
hookEntity
.
backup
==
null
)
return
;
return
false
;
ensureMethodDeclaringClass
(
originMethod
,
hookEntity
.
backup
);
return
ensureMethodDeclaringClass
(
originMethod
,
hookEntity
.
backup
);
}
}
private
static
void
resolveStaticMethod
(
Member
method
)
{
private
static
void
resolveStaticMethod
(
Member
method
)
{
...
@@ -248,7 +277,7 @@ public class SandHook {
...
@@ -248,7 +277,7 @@ public class SandHook {
public
static
native
void
ensureMethodCached
(
Method
hook
,
Method
backup
);
public
static
native
void
ensureMethodCached
(
Method
hook
,
Method
backup
);
public
static
native
void
ensureMethodDeclaringClass
(
Member
originMethod
,
Method
backupMethod
);
public
static
native
boolean
ensureMethodDeclaringClass
(
Member
originMethod
,
Method
backupMethod
);
@FunctionalInterface
@FunctionalInterface
public
interface
HookModeCallBack
{
public
interface
HookModeCallBack
{
...
...
xposedcompat/src/main/java/com/swift/sandhook/xposedcompat/XposedCompat.java
View file @
3e4cf9aa
...
@@ -20,11 +20,13 @@ public class XposedCompat {
...
@@ -20,11 +20,13 @@ public class XposedCompat {
public
static
File
cacheDir
;
public
static
File
cacheDir
;
public
static
Context
context
;
public
static
Context
context
;
public
static
ClassLoader
classLoader
;
public
static
volatile
ClassLoader
classLoader
;
public
static
String
packageName
;
public
static
String
packageName
;
public
static
String
processName
;
public
static
String
processName
;
public
static
boolean
isFirstApplication
;
public
static
boolean
isFirstApplication
;
public
static
volatile
boolean
retryWhenCallOriginError
=
false
;
private
static
ClassLoader
sandHookXposedClassLoader
;
private
static
ClassLoader
sandHookXposedClassLoader
;
...
...
xposedcompat/src/main/java/com/swift/sandhook/xposedcompat/methodgen/ErrorCatch.java
0 → 100644
View file @
3e4cf9aa
package
com
.
swift
.
sandhook
.
xposedcompat
.
methodgen
;
import
android.util.Log
;
import
com.swift.sandhook.SandHook
;
import
com.swift.sandhook.xposedcompat.XposedCompat
;
import
java.lang.reflect.Member
;
import
java.lang.reflect.Method
;
public
class
ErrorCatch
{
public
static
Object
callOriginError
(
Member
originMethod
,
Method
backupMethod
,
Object
thiz
,
Object
[]
args
)
throws
Throwable
{
if
(
XposedCompat
.
retryWhenCallOriginError
)
{
Log
.
w
(
"SandHook"
,
"method <"
+
originMethod
.
toString
()
+
"> use invoke to call origin!"
);
return
SandHook
.
callOriginMethod
(
originMethod
,
backupMethod
,
thiz
,
args
);
}
else
{
return
null
;
}
}
}
xposedcompat/src/main/java/com/swift/sandhook/xposedcompat/methodgen/HookerDexMaker.java
View file @
3e4cf9aa
package
com
.
swift
.
sandhook
.
xposedcompat
.
methodgen
;
package
com
.
swift
.
sandhook
.
xposedcompat
.
methodgen
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
com.android.dx.BinaryOp
;
import
com.android.dx.BinaryOp
;
import
com.android.dx.Code
;
import
com.android.dx.Code
;
...
@@ -43,6 +44,7 @@ public class HookerDexMaker {
...
@@ -43,6 +44,7 @@ public class HookerDexMaker {
private
static
final
String
CLASS_NAME_PREFIX
=
"SandHooker"
;
private
static
final
String
CLASS_NAME_PREFIX
=
"SandHooker"
;
private
static
final
String
FIELD_NAME_HOOK_INFO
=
"additionalHookInfo"
;
private
static
final
String
FIELD_NAME_HOOK_INFO
=
"additionalHookInfo"
;
private
static
final
String
FIELD_NAME_METHOD
=
"method"
;
private
static
final
String
FIELD_NAME_METHOD
=
"method"
;
private
static
final
String
FIELD_NAME_BACKUP_METHOD
=
"backupMethod"
;
private
static
final
String
PARAMS_FIELD_NAME_METHOD
=
"method"
;
private
static
final
String
PARAMS_FIELD_NAME_METHOD
=
"method"
;
private
static
final
String
PARAMS_FIELD_NAME_THIS_OBJECT
=
"thisObject"
;
private
static
final
String
PARAMS_FIELD_NAME_THIS_OBJECT
=
"thisObject"
;
private
static
final
String
PARAMS_FIELD_NAME_ARGS
=
"args"
;
private
static
final
String
PARAMS_FIELD_NAME_ARGS
=
"args"
;
...
@@ -50,6 +52,7 @@ public class HookerDexMaker {
...
@@ -50,6 +52,7 @@ public class HookerDexMaker {
private
static
final
String
CALLBACK_METHOD_NAME_AFTER
=
"callAfterHookedMethod"
;
private
static
final
String
CALLBACK_METHOD_NAME_AFTER
=
"callAfterHookedMethod"
;
private
static
final
TypeId
<
Throwable
>
throwableTypeId
=
TypeId
.
get
(
Throwable
.
class
);
private
static
final
TypeId
<
Throwable
>
throwableTypeId
=
TypeId
.
get
(
Throwable
.
class
);
private
static
final
TypeId
<
Member
>
memberTypeId
=
TypeId
.
get
(
Member
.
class
);
private
static
final
TypeId
<
Member
>
memberTypeId
=
TypeId
.
get
(
Member
.
class
);
private
static
final
TypeId
<
Method
>
methodTypeId
=
TypeId
.
get
(
Method
.
class
);
private
static
final
TypeId
<
XC_MethodHook
>
callbackTypeId
=
TypeId
.
get
(
XC_MethodHook
.
class
);
private
static
final
TypeId
<
XC_MethodHook
>
callbackTypeId
=
TypeId
.
get
(
XC_MethodHook
.
class
);
private
static
final
TypeId
<
XposedBridge
.
AdditionalHookInfo
>
hookInfoTypeId
private
static
final
TypeId
<
XposedBridge
.
AdditionalHookInfo
>
hookInfoTypeId
=
TypeId
.
get
(
XposedBridge
.
AdditionalHookInfo
.
class
);
=
TypeId
.
get
(
XposedBridge
.
AdditionalHookInfo
.
class
);
...
@@ -79,10 +82,12 @@ public class HookerDexMaker {
...
@@ -79,10 +82,12 @@ public class HookerDexMaker {
private
FieldId
<?,
XposedBridge
.
AdditionalHookInfo
>
mHookInfoFieldId
;
private
FieldId
<?,
XposedBridge
.
AdditionalHookInfo
>
mHookInfoFieldId
;
private
FieldId
<?,
Member
>
mMethodFieldId
;
private
FieldId
<?,
Member
>
mMethodFieldId
;
private
FieldId
<?,
Method
>
mBackupMethodFieldId
;
private
MethodId
<?,
?>
mBackupMethodId
;
private
MethodId
<?,
?>
mBackupMethodId
;
private
MethodId
<?,
?>
mCallBackupMethodId
;
private
MethodId
<?,
?>
mCallBackupMethodId
;
private
MethodId
<?,
?>
mHookMethodId
;
private
MethodId
<?,
?>
mHookMethodId
;
private
MethodId
<?,
?>
mPrintLogMethodId
;
private
MethodId
<?,
?>
mPrintLogMethodId
;
private
MethodId
<?,
?>
mSandHookCallOriginMethodId
;
private
TypeId
<?>
mHookerTypeId
;
private
TypeId
<?>
mHookerTypeId
;
private
TypeId
<?>[]
mParameterTypeIds
;
private
TypeId
<?>[]
mParameterTypeIds
;
...
@@ -215,11 +220,10 @@ public class HookerDexMaker {
...
@@ -215,11 +220,10 @@ public class HookerDexMaker {
private
HookWrapper
.
HookEntity
loadHookerClass
(
ClassLoader
loader
,
String
className
)
throws
Exception
{
private
HookWrapper
.
HookEntity
loadHookerClass
(
ClassLoader
loader
,
String
className
)
throws
Exception
{
mHookClass
=
loader
.
loadClass
(
className
);
mHookClass
=
loader
.
loadClass
(
className
);
// Execute our newly-generated code in-process.
// Execute our newly-generated code in-process.
mHookClass
.
getMethod
(
METHOD_NAME_SETUP
,
Member
.
class
,
XposedBridge
.
AdditionalHookInfo
.
class
)
.
invoke
(
null
,
mMember
,
mHookInfo
);
mHookMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_HOOK
,
mActualParameterTypes
);
mHookMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_HOOK
,
mActualParameterTypes
);
mBackupMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_BACKUP
,
mActualParameterTypes
);
mBackupMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_BACKUP
,
mActualParameterTypes
);
mCallBackupMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_CALL_BACKUP
,
mActualParameterTypes
);
mCallBackupMethod
=
mHookClass
.
getMethod
(
METHOD_NAME_CALL_BACKUP
,
mActualParameterTypes
);
mHookClass
.
getMethod
(
METHOD_NAME_SETUP
,
Member
.
class
,
Method
.
class
,
XposedBridge
.
AdditionalHookInfo
.
class
).
invoke
(
null
,
mMember
,
mBackupMethod
,
mHookInfo
);
return
new
HookWrapper
.
HookEntity
(
mMember
,
mHookMethod
,
mBackupMethod
);
return
new
HookWrapper
.
HookEntity
(
mMember
,
mHookMethod
,
mBackupMethod
);
}
}
...
@@ -246,42 +250,107 @@ public class HookerDexMaker {
...
@@ -246,42 +250,107 @@ public class HookerDexMaker {
private
void
generateFields
()
{
private
void
generateFields
()
{
mHookInfoFieldId
=
mHookerTypeId
.
getField
(
hookInfoTypeId
,
FIELD_NAME_HOOK_INFO
);
mHookInfoFieldId
=
mHookerTypeId
.
getField
(
hookInfoTypeId
,
FIELD_NAME_HOOK_INFO
);
mMethodFieldId
=
mHookerTypeId
.
getField
(
memberTypeId
,
FIELD_NAME_METHOD
);
mMethodFieldId
=
mHookerTypeId
.
getField
(
memberTypeId
,
FIELD_NAME_METHOD
);
mBackupMethodFieldId
=
mHookerTypeId
.
getField
(
methodTypeId
,
FIELD_NAME_BACKUP_METHOD
);
mDexMaker
.
declare
(
mHookInfoFieldId
,
Modifier
.
STATIC
,
null
);
mDexMaker
.
declare
(
mHookInfoFieldId
,
Modifier
.
STATIC
,
null
);
mDexMaker
.
declare
(
mMethodFieldId
,
Modifier
.
STATIC
,
null
);
mDexMaker
.
declare
(
mMethodFieldId
,
Modifier
.
STATIC
,
null
);
mDexMaker
.
declare
(
mBackupMethodFieldId
,
Modifier
.
STATIC
,
null
);
}
}
private
void
generateSetupMethod
()
{
private
void
generateSetupMethod
()
{
MethodId
<?,
Void
>
setupMethodId
=
mHookerTypeId
.
getMethod
(
MethodId
<?,
Void
>
setupMethodId
=
mHookerTypeId
.
getMethod
(
TypeId
.
VOID
,
METHOD_NAME_SETUP
,
memberTypeId
,
hookInfoTypeId
);
TypeId
.
VOID
,
METHOD_NAME_SETUP
,
memberTypeId
,
methodTypeId
,
hookInfoTypeId
);
Code
code
=
mDexMaker
.
declare
(
setupMethodId
,
Modifier
.
PUBLIC
|
Modifier
.
STATIC
);
Code
code
=
mDexMaker
.
declare
(
setupMethodId
,
Modifier
.
PUBLIC
|
Modifier
.
STATIC
);
// init logic
// init logic
// get parameters
// get parameters
Local
<
Member
>
method
=
code
.
getParameter
(
0
,
memberTypeId
);
Local
<
Member
>
method
=
code
.
getParameter
(
0
,
memberTypeId
);
Local
<
XposedBridge
.
AdditionalHookInfo
>
hookInfo
=
code
.
getParameter
(
1
,
hookInfoTypeId
);
Local
<
Method
>
backupMethod
=
code
.
getParameter
(
1
,
methodTypeId
);
Local
<
XposedBridge
.
AdditionalHookInfo
>
hookInfo
=
code
.
getParameter
(
2
,
hookInfoTypeId
);
// save params to static
// save params to static
code
.
sput
(
mMethodFieldId
,
method
);
code
.
sput
(
mMethodFieldId
,
method
);
code
.
sput
(
mBackupMethodFieldId
,
backupMethod
);
code
.
sput
(
mHookInfoFieldId
,
hookInfo
);
code
.
sput
(
mHookInfoFieldId
,
hookInfo
);
code
.
returnVoid
();
code
.
returnVoid
();
}
}
private
void
generateBackupMethod
()
{
private
void
generateBackupMethod
()
{
mBackupMethodId
=
mHookerTypeId
.
getMethod
(
mReturnTypeId
,
METHOD_NAME_BACKUP
,
mParameterTypeIds
);
mBackupMethodId
=
mHookerTypeId
.
getMethod
(
mReturnTypeId
,
METHOD_NAME_BACKUP
,
mParameterTypeIds
);
mSandHookCallOriginMethodId
=
TypeId
.
get
(
ErrorCatch
.
class
).
getMethod
(
TypeId
.
get
(
Object
.
class
),
"callOriginError"
,
memberTypeId
,
methodTypeId
,
TypeId
.
get
(
Object
.
class
),
TypeId
.
get
(
Object
[].
class
));
MethodId
<?,
?>
errLogMethod
=
TypeId
.
get
(
DexLog
.
class
).
getMethod
(
TypeId
.
get
(
Void
.
TYPE
),
"printCallOriginError"
,
TypeId
.
get
(
Member
.
class
));
Code
code
=
mDexMaker
.
declare
(
mBackupMethodId
,
Modifier
.
PUBLIC
|
Modifier
.
STATIC
);
Code
code
=
mDexMaker
.
declare
(
mBackupMethodId
,
Modifier
.
PUBLIC
|
Modifier
.
STATIC
);
Local
<
Member
>
method
=
code
.
newLocal
(
memberTypeId
);
Local
<
Member
>
method
=
code
.
newLocal
(
memberTypeId
);
Local
<
Method
>
backupMethod
=
code
.
newLocal
(
methodTypeId
);
Local
<
Object
>
thisObject
=
code
.
newLocal
(
TypeId
.
OBJECT
);
Local
<
Object
[]>
args
=
code
.
newLocal
(
objArrayTypeId
);
Local
<
Integer
>
actualParamSize
=
code
.
newLocal
(
TypeId
.
INT
);
Local
<
Integer
>
argIndex
=
code
.
newLocal
(
TypeId
.
INT
);
Local
<
Object
>
resultObj
=
code
.
newLocal
(
TypeId
.
OBJECT
);
Label
tryCatchBlock
=
new
Label
();
Local
[]
allArgsLocals
=
createParameterLocals
(
code
);
Map
<
TypeId
,
Local
>
resultLocals
=
createResultLocals
(
code
);
Map
<
TypeId
,
Local
>
resultLocals
=
createResultLocals
(
code
);
MethodId
<?,
?>
errLogMethod
=
TypeId
.
get
(
DexLog
.
class
).
getMethod
(
TypeId
.
get
(
Void
.
TYPE
),
"printCallOriginError"
,
TypeId
.
get
(
Member
.
class
));
//very very important!!!!!!!!!!!
//very very important!!!!!!!!!!!
//add a try cache block avoid inline
//add a try cache block avoid inline
Label
tryCatchBlock
=
new
Label
();
code
.
sget
(
mMethodFieldId
,
method
);
code
.
invokeStatic
(
errLogMethod
,
null
,
method
);
// start of try
// start of try
code
.
addCatchClause
(
throwableTypeId
,
tryCatchBlock
);
code
.
addCatchClause
(
throwableTypeId
,
tryCatchBlock
);
code
.
sget
(
mMethodFieldId
,
method
);
code
.
invokeStatic
(
errLogMethod
,
null
,
method
);
//call origin by invoke
code
.
loadConstant
(
args
,
null
);
code
.
loadConstant
(
argIndex
,
0
);
code
.
sget
(
mBackupMethodFieldId
,
backupMethod
);
int
paramsSize
=
mParameterTypeIds
.
length
;
int
offset
=
0
;
// thisObject
if
(
mIsStatic
)
{
// thisObject = null
code
.
loadConstant
(
thisObject
,
null
);
}
else
{
// thisObject = args[0]
offset
=
1
;
code
.
move
(
thisObject
,
allArgsLocals
[
0
]);
}
// offset = mIsStatic ? 0 : 1;
// for (int i = offset; i < allArgsLocals.length; i++) {
// code.loadConstant(argIndex, i - offset);
// code.aget(resultObj, args, argIndex);
// autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true);
// }
// actual args (exclude thisObject if this is not a static method)
code
.
loadConstant
(
actualParamSize
,
paramsSize
-
offset
);
code
.
newArray
(
args
,
actualParamSize
);
for
(
int
i
=
offset
;
i
<
paramsSize
;
i
++)
{
Local
parameter
=
allArgsLocals
[
i
];
// save parameter to resultObj as Object
autoBoxIfNecessary
(
code
,
resultObj
,
parameter
);
code
.
loadConstant
(
argIndex
,
i
-
offset
);
// save Object to args
code
.
aput
(
args
,
argIndex
,
resultObj
);
}
if
(
mReturnTypeId
.
equals
(
TypeId
.
VOID
))
{
code
.
invokeStatic
(
mSandHookCallOriginMethodId
,
null
,
method
,
backupMethod
,
thisObject
,
args
);
code
.
returnVoid
();
}
else
{
code
.
invokeStatic
(
mSandHookCallOriginMethodId
,
resultObj
,
method
,
backupMethod
,
thisObject
,
args
);
TypeId
objTypeId
=
getObjTypeIdIfPrimitive
(
mReturnTypeId
);
Local
matchObjLocal
=
resultLocals
.
get
(
objTypeId
);
code
.
cast
(
matchObjLocal
,
resultObj
);
// have to use matching typed Object(Integer, Double ...) to do unboxing
Local
toReturn
=
resultLocals
.
get
(
mReturnTypeId
);
autoUnboxIfNecessary
(
code
,
toReturn
,
matchObjLocal
,
resultLocals
,
true
);
code
.
returnValue
(
toReturn
);
}
code
.
mark
(
tryCatchBlock
);
// do nothing
// do nothing
if
(
mReturnTypeId
.
equals
(
TypeId
.
VOID
))
{
if
(
mReturnTypeId
.
equals
(
TypeId
.
VOID
))
{
code
.
returnVoid
();
code
.
returnVoid
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment