Skip to content

Commit f6376e7

Browse files
authored
[cppia] Fix missing jit exception checks for method calls (#1212)
* [cppia] Add missing exception checks In interp mode, we need to stop immediately if a jit exception has been thrown. * [cppia] Test for method on exception throwing expr * [cppia] Add test for host class method call This covers the CallHaxe bug * [cppia] Fix throw/catch of seg faults in tests The host must be compiled with HXCPP_CATCH_SEGV so that seg faults are handled as hxcpp exceptions. * [tests] Verify CallHaxe BCR after this check This test verifies that the full BCR check is needed in CallHaxe::run after runObject, otherwise the return is ignored and arguments continue to be evaluated * [tests] Verify CallHaxe BCR after each arg * [tests] Verify Call with return as function * [tests] Verify CallMemberVTable with this return
1 parent 1fd3680 commit f6376e7

File tree

7 files changed

+205
-13
lines changed

7 files changed

+205
-13
lines changed

src/hx/cppia/Cppia.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,7 @@ struct CallHaxe : public CppiaExpr
17611761
{
17621762
unsigned char *pointer = ctx->pointer;
17631763
ctx->pushObject(isStatic ? 0: thisExpr ? thisExpr->runObject(ctx) : ctx->getThis(false));
1764+
BCR_VCHECK;
17641765

17651766
const char *s = function.signature+1;
17661767
for(int a=0;a<args.size();a++)
@@ -1776,6 +1777,7 @@ struct CallHaxe : public CppiaExpr
17761777
case sigObject: ctx->pushObject( arg->runObject(ctx) ); break;
17771778
default: ;// huh?
17781779
}
1780+
BCR_VCHECK;
17791781
}
17801782

17811783
AutoStack a(ctx,pointer);
@@ -2165,8 +2167,9 @@ struct CallMemberVTable : public CppiaExpr
21652167
ExprType getType() { return returnType; }
21662168
// ScriptCallable **vtable = (ScriptCallable **)thisVal->__GetScriptVTable();
21672169

2168-
#define CALL_VTABLE_SETUP \
2170+
#define CALL_VTABLE_SETUP(errorValue) \
21692171
hx::Object *thisVal = thisExpr ? thisExpr->runObject(ctx) : ctx->getThis(); \
2172+
BCR_CHECK_RET(errorValue); \
21702173
CPPIA_CHECK(thisVal); \
21712174
ScriptCallable **vtable = (!isInterfaceCall ? (*(ScriptCallable ***)((char *)thisVal +scriptVTableOffset)) : (ScriptCallable **) thisVal->__GetScriptVTable()); \
21722175
unsigned char *pointer = ctx->pointer; \
@@ -2177,29 +2180,29 @@ struct CallMemberVTable : public CppiaExpr
21772180

21782181
void runVoid(CppiaCtx *ctx)
21792182
{
2180-
CALL_VTABLE_SETUP
2183+
CALL_VTABLE_SETUP()
21812184
ctx->runVoid(func);
21822185
}
21832186
int runInt(CppiaCtx *ctx)
21842187
{
2185-
CALL_VTABLE_SETUP
2186-
return runContextConvertInt(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
2188+
CALL_VTABLE_SETUP(BCRReturn())
2189+
return runContextConvertInt(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
21872190
}
2188-
2191+
21892192
Float runFloat(CppiaCtx *ctx)
21902193
{
2191-
CALL_VTABLE_SETUP
2192-
return runContextConvertFloat(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
2194+
CALL_VTABLE_SETUP(BCRReturn())
2195+
return runContextConvertFloat(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
21932196
}
21942197
String runString(CppiaCtx *ctx)
21952198
{
2196-
CALL_VTABLE_SETUP
2197-
return runContextConvertString(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
2199+
CALL_VTABLE_SETUP(BCRReturn())
2200+
return runContextConvertString(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
21982201
}
21992202
hx::Object *runObject(CppiaCtx *ctx)
22002203
{
2201-
CALL_VTABLE_SETUP
2202-
return runContextConvertObject(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
2204+
CALL_VTABLE_SETUP(BCRReturn())
2205+
return runContextConvertObject(ctx, checkInterfaceReturnType ? func->getReturnType() : returnType, func);
22032206
}
22042207

22052208
bool isBoolInt() { return boolResult; }
@@ -3109,6 +3112,8 @@ struct Call : public CppiaDynamicExpr
31093112
hx::Object *runObject(CppiaCtx *ctx)
31103113
{
31113114
hx::Object *funcVal = func->runObject(ctx);
3115+
BCR_CHECK;
3116+
31123117
CPPIA_CHECK_FUNC(funcVal);
31133118

31143119
int size = args.size();

src/hx/cppia/Cppia.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ struct BCRReturn
842842

843843

844844
#define BCR_CHECK if (ctx->breakContReturn || ctx->exception) return BCRReturn();
845-
#define BCR_CHECK_RET(x) if (ctx->breakContReturn) return x;
845+
#define BCR_CHECK_RET(x) if (ctx->breakContReturn || ctx->exception) return x;
846846
#define BCR_VCHECK if (ctx->breakContReturn || ctx->exception) return;
847847

848848

test/cppia/Client.hx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,55 @@ class Client
189189
default:
190190
}
191191

192+
switch LocalFunctionExceptions.testObjMethodOnReturn() {
193+
case Error(message):
194+
Common.status = 'Failed test for running object method on returned value: ' + message;
195+
return;
196+
default:
197+
}
198+
199+
switch LocalFunctionExceptions.testClassMethodOnReturn() {
200+
case Error(message):
201+
Common.status = 'Failed test for running class method on returned value: ' + message;
202+
return;
203+
default:
204+
}
205+
206+
switch LocalFunctionExceptions.testHostClassMethodOnHostReturn() {
207+
case Error(message):
208+
Common.status = 'Failed test for running host class method on returned value: ' + message;
209+
return;
210+
default:
211+
}
212+
213+
switch ReturnExpressions.testHostThisReturn() {
214+
case Error(message):
215+
Common.status = 'Failed test for host this return stopping argument evaluation: ' + message;
216+
return;
217+
default:
218+
}
219+
220+
switch ReturnExpressions.testHostArgReturn() {
221+
case Error(message):
222+
Common.status = 'Failed test for argument return stopping argument evaluation: ' + message;
223+
return;
224+
default:
225+
}
226+
227+
switch ReturnExpressions.testClientThisReturn() {
228+
case Error(message):
229+
Common.status = 'Failed test for client this return stopping evaluation: ' + message;
230+
return;
231+
default:
232+
}
233+
234+
switch ReturnExpressions.testFuncReturn() {
235+
case Error(message):
236+
Common.status = 'Failed test for function value return stopping evaluation: ' + message;
237+
return;
238+
default:
239+
}
240+
192241
// regression test for #926
193242
var x:Dynamic = 3;
194243
x *= 5;

test/cppia/Common.hx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,17 @@ class Common
88
public static var callbackSet:Int = 0;
99
public static var callback: Void->Void;
1010

11+
public static var count = 0;
12+
public static function incrementCount():Int {
13+
return count++;
14+
}
15+
16+
public function new() {}
17+
18+
public function dummyMethod() {}
19+
public function dummyMethodArg(_) {}
20+
21+
public function instanceIncrementCount(_) {
22+
count++;
23+
}
1124
}

test/cppia/LocalFunctionExceptions.hx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ enum Status {
33
Error(message:String);
44
}
55

6+
class DummyClass {
7+
public function run() {}
8+
}
9+
610
class LocalFunctionExceptions {
7-
static function staticFunction() {
11+
static function staticFunction():Dynamic {
812
throw 'Thrown from static';
913
}
1014

@@ -65,4 +69,58 @@ class LocalFunctionExceptions {
6569

6670
return Error("No exception caught");
6771
}
72+
73+
public static function testObjMethodOnReturn():Status {
74+
function localFunction() {
75+
(staticFunction() : Dynamic).run();
76+
}
77+
78+
try {
79+
localFunction();
80+
} catch (e:Dynamic) {
81+
if (e == 'Thrown from static') {
82+
return Ok;
83+
} else {
84+
return Error("Incorrect exception caught from local function call: " + e);
85+
}
86+
}
87+
88+
return Error("No exception caught");
89+
}
90+
91+
public static function testClassMethodOnReturn():Status {
92+
function localFunction() {
93+
(staticFunction() : DummyClass).run();
94+
}
95+
96+
try {
97+
localFunction();
98+
} catch (e:Dynamic) {
99+
if (e == 'Thrown from static') {
100+
return Ok;
101+
} else {
102+
return Error("Incorrect exception caught from local function call: " + e);
103+
}
104+
}
105+
106+
return Error("No exception caught");
107+
}
108+
109+
public static function testHostClassMethodOnHostReturn():Status {
110+
function localFunction() {
111+
(staticFunction() : Common).dummyMethod();
112+
}
113+
114+
try {
115+
localFunction();
116+
} catch (e:Dynamic) {
117+
if (e == 'Thrown from static') {
118+
return Ok;
119+
} else {
120+
return Error("Incorrect exception caught from local function call: " + e);
121+
}
122+
}
123+
124+
return Error("No exception caught");
125+
}
68126
}

test/cppia/ReturnExpressions.hx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import LocalFunctionExceptions.Status;
2+
3+
class ReturnExpressions {
4+
// prevent the call from being optimised out due to unconditional return
5+
@:analyzer(ignore)
6+
static function returnAsCallHaxeThis() {
7+
(cast return:Common).dummyMethodArg(Common.incrementCount());
8+
}
9+
10+
public static function testHostThisReturn() {
11+
Common.count = 0;
12+
returnAsCallHaxeThis();
13+
if (Common.count == 0) {
14+
return Ok;
15+
}
16+
return Error("Host method executed after return");
17+
}
18+
19+
@:analyzer(ignore)
20+
static function returnAsCallHaxeArg() {
21+
new Common().instanceIncrementCount(return);
22+
}
23+
24+
public static function testHostArgReturn() {
25+
Common.count = 0;
26+
returnAsCallHaxeArg();
27+
if (Common.count == 0) {
28+
return Ok;
29+
}
30+
return Error("Host method executed after return");
31+
}
32+
33+
@:analyzer(ignore)
34+
static function returnAsFunction() {
35+
// if return is not handled, there is no function to run so this will
36+
// give a null function exception
37+
(cast return : (Int) -> Void)(Common.incrementCount());
38+
}
39+
40+
public static function testFuncReturn() {
41+
Common.count = 0;
42+
returnAsFunction();
43+
if (Common.count == 0) {
44+
return Ok;
45+
}
46+
return Error("Host method executed after return");
47+
}
48+
49+
function vtableMethod(_) {}
50+
51+
@:analyzer(ignore)
52+
public static function returnAsClientThis() {
53+
// if return is not handled, there is no instance to run the method with
54+
// so this will give a null function exception
55+
(cast return:ReturnExpressions).vtableMethod(Common.incrementCount());
56+
}
57+
58+
public static function testClientThisReturn() {
59+
Common.count = 0;
60+
returnAsClientThis();
61+
if (Common.count == 0) {
62+
return Ok;
63+
}
64+
return Error("Host method executed after return");
65+
}
66+
}

test/cppia/compile-host.hxml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ HostExtendedRoot
55
-L utest
66
--dce no
77
--cpp bin
8+
-D HXCPP_CATCH_SEGV

0 commit comments

Comments
 (0)