Skip to content

Commit

Permalink
Fix several issues related to exceptions, including gnue#7 and gnue#8
Browse files Browse the repository at this point in the history
Exceptions are now propagated through saved vm environments (gnue#8).
Pop-handler and pop are generated in the right sequence, following NewtonOS
compiler.
Fix si_set_lex_scope for native functions.
Add unit tests for various cases.
Add BinEqual function for tests.
  • Loading branch information
pguyot committed Jul 9, 2020
1 parent 6234fed commit b80abc3
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 74 deletions.
14 changes: 13 additions & 1 deletion sample/exception.newt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ begin
Throw('|evt.ex.foo;type.ref.frame|, {});
onexception |evt.ex.foo| do
Print("Caught custom with data\n");

// Catch and print data.
try
Throw('|evt.ex.foo;type.ref.string|, "Some data");
onexception |evt.ex.foo| do
Print("Caught: " & CurrentException().data & "\n");

// Catch custom with subexception.
try
Throw('|evt.ex.foo.sub|, nil);
onexception |evt.ex.foo| do
Print("Caught custom sub\n");

// Catch custom with subexception and Apply
try
Apply(GetGlobalFn('Throw), ['|evt.ex.foo.sub|, nil]);
onexception |evt.ex.foo| do
Print("Caught custom sub with apply\n");

// Don't catch.
try
Expand Down
90 changes: 56 additions & 34 deletions src/newt_core/NewtBC.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ static int16_t NBCMakeFnArgs(newtRefArg fn, nps_syntax_node_t * stree, nps_nod
static nbc_env_t * NBCMakeFnEnv(nps_syntax_node_t * stree, nps_node_t args);
static uint32_t NBCGenBranch(uint8_t a);
static void NBCDefLocal(newtRefArg type, newtRefArg r, bool init);
static void NBCBackPatch(uint32_t cx, int16_t b);
static void NBCBackPatch(uint32_t cx, uintptr_t b);
static void NBCPushBreakStack(uint32_t cx);
static void NBCBreakBackPatchs(uint32_t loop_head, uint32_t cx);
static void NBCPushOnexcpStack(uint32_t cx);
static void NBCOnexcpBackPatchs(uint32_t try_head, uint32_t cx);
static void NBCGenOnexcpPC(int32_t pc);
static void NBCOnexcpBackPatchs(uint32_t try_head);
static void NBCGenOnexcpPC(void);
static void NBCGenOnexcpBranch(void);
static void NBCOnexcpBackPatchL(uint32_t sp, int32_t pc);

Expand All @@ -142,8 +142,8 @@ static void NBCGenGlobalVar(nps_syntax_node_t * stree, nps_node_t r);
static void NBCGenLocalVar(nps_syntax_node_t * stree, nps_node_t type, nps_node_t r);
static bool NBCTypeValid(nps_node_t type);
static int16_t NBCGenTryPre(nps_syntax_node_t * stree, nps_node_t r);
static int16_t NBCGenTryPost(nps_syntax_node_t * stree, nps_node_t r, uint32_t * onexcpspP);
static void NBCGenTry(nps_syntax_node_t * stree, nps_node_t expr, nps_node_t onexception_list);
static int16_t NBCGenTryPost(nps_syntax_node_t * stree, nps_node_t r, uint32_t * onexcpspP, bool ret);
static void NBCGenTry(nps_syntax_node_t * stree, nps_node_t expr, nps_node_t onexception_list, bool ret);
static void NBCGenIfThenElse(nps_syntax_node_t * stree, nps_node_t cond, nps_node_t thenelse, bool ret);
static void NBCGenAnd(nps_syntax_node_t * stree, nps_node_t op1, nps_node_t op2);
static void NBCGenOr(nps_syntax_node_t * stree, nps_node_t op1, nps_node_t op2);
Expand Down Expand Up @@ -222,7 +222,13 @@ void NBCGenCodeEnv(nbc_env_t * env, uint8_t a, int16_t b)
bc = ENV_BC(env);

if (a == kNBCFieldMask)
b = 1;
b = 7;
// pop-handler is 07 00 07
// Quoting Walter Smith:
// Due to historical stupidity, the pophandlers instruction is the
// nonexistent eighth unary1op. Its encoding is 07 00 07!
// https://github.com/wrs/prota/blob/a07c772bce39c5153687dd853206c0803dabdf05/runtime/interpreter.cpp#L593
// NEWT/0 and @wrs' Prota don't care, but NewtonOS might.

if (a != kNBCFieldMask &&
((a & kNBCFieldMask) == a ||
Expand Down Expand Up @@ -599,10 +605,27 @@ void NBCDefLocal(newtRefArg type, newtRefArg r, bool init)
* @note 分岐命令やループ命令などすぐにオペデータが決定しない場合に使う
*/

void NBCBackPatch(uint32_t cx, int16_t b)
{
BC[cx + 1] = b >> 8;
BC[cx + 2] = b & 0xff;
void NBCBackPatch(uint32_t cx, uintptr_t b)
{
// If value fits on 16 bits, simply write it
// Otherwise, add it to literals and modify previous instruction.
// Values might not fit if it is a push constant of an integer > 8192
// which may happen for exception handlers (branches are limited to 2^16
// so exceptions should be possible up to 2^16).
if (b < 0x10000) {
BC[cx + 1] = b >> 8;
BC[cx + 2] = b & 0xff;
} else {
if (BC[cx] == (kNBCPushConstant | kNBCFieldMask)) {
// push-constant
ssize_t ix = NewtFindArrayIndex(newt_bc_env->literals, b, 0);
if (ix == -1) // リテラルに追加
ix = NBCAddLiteralEnv(newt_bc_env, b);
BC[cx + 1] = ix >> 8;
BC[cx + 2] = ix & 0xff;
BC[cx] = kNBCPush | kNBCFieldMask;
}
}
}


Expand Down Expand Up @@ -689,7 +712,7 @@ void NBCPushOnexcpStack(uint32_t cx)
* @return なし
*/

void NBCOnexcpBackPatchs(uint32_t try_head, uint32_t cx)
void NBCOnexcpBackPatchs(uint32_t try_head)
{
uint32_t branch;

Expand All @@ -700,28 +723,29 @@ void NBCOnexcpBackPatchs(uint32_t try_head, uint32_t cx)
if (branch < try_head)
break;

NBCBackPatch(branch, cx); // ブランチをバックパッチ
if (branch + 3 == CX) {
// Simplify and remove branch.
CX -= 3;
} else {
NBCBackPatch(branch, CX); // ブランチをバックパッチ
}
}
}


/*------------------------------------------------------------------------*/
/** 例外処理命令のバイトコードを生成する
*
* @param pc [in] 例外処理命令のプログラムカウンタ
*
* @return なし
*/

void NBCGenOnexcpPC(int32_t pc)
void NBCGenOnexcpPC(void)
{
newtRefVar r;
int16_t b;

r = NewtMakeInteger(pc);
b = NBCGenPushLiteral(r);
uint32_t cx = CX;

NBCPushOnexcpStack(b); // バックパッチのためにスタックにプッシュする
NBCGenCode(kNBCPushConstant, 0xFFFF); // push constant, later patched
// as push literal if required.
NBCPushOnexcpStack(cx); // バックパッチのためにスタックにプッシュする
}


Expand Down Expand Up @@ -757,7 +781,7 @@ void NBCOnexcpBackPatchL(uint32_t sp, int32_t pc)
return;

r = NewtMakeInteger(pc);
NewtSetArraySlot(LITERALS, ONEXCPSTACK[sp], r);
NBCBackPatch(ONEXCPSTACK[sp], r);
}


Expand Down Expand Up @@ -1159,7 +1183,7 @@ int16_t NBCGenTryPre(nps_syntax_node_t * stree, nps_node_t r)
{
case kNPSOnexception:
NBCGenPUSH(node->op1); // シンボル
NBCGenOnexcpPC(-1); // PC(ダミー)
NBCGenOnexcpPC();

numExcps = 1;
break;
Expand All @@ -1186,7 +1210,7 @@ int16_t NBCGenTryPre(nps_syntax_node_t * stree, nps_node_t r)
*/

int16_t NBCGenTryPost(nps_syntax_node_t * stree, nps_node_t r,
uint32_t * onexcpspP)
uint32_t * onexcpspP, bool ret)
{
int16_t numExcps = 0;

Expand All @@ -1204,15 +1228,15 @@ int16_t NBCGenTryPost(nps_syntax_node_t * stree, nps_node_t r,
(*onexcpspP)++;

// onexception のコード生成
NBCGenBC_stmt(stree, node->op2, true);
NBCGenBC_stmt(stree, node->op2, ret);
NBCGenOnexcpBranch();

numExcps = 1;
break;

case kNPSOnexceptionList:
numExcps = NBCGenTryPost(stree, node->op1, onexcpspP)
+ NBCGenTryPost(stree, node->op2, onexcpspP);
numExcps = NBCGenTryPost(stree, node->op1, onexcpspP, ret)
+ NBCGenTryPost(stree, node->op2, onexcpspP, ret);
break;
}
}
Expand All @@ -1232,7 +1256,7 @@ int16_t NBCGenTryPost(nps_syntax_node_t * stree, nps_node_t r,
*/

void NBCGenTry(nps_syntax_node_t * stree, nps_node_t expr,
nps_node_t onexception_list)
nps_node_t onexception_list, bool ret)
{
uint32_t onexcp_cx;
uint32_t branch_cx;
Expand All @@ -1244,17 +1268,17 @@ void NBCGenTry(nps_syntax_node_t * stree, nps_node_t expr,
NBCGenCode(kNBCNewHandlers, numExcps);

// 実行文
NBCGenBC_op(stree, expr);
NBCGenBC_stmt(stree, expr, ret);
NBCGenCode(kNBCPopHandlers, 0);

branch_cx = NBCGenBranch(kNBCBranch);

// onexception
onexcp_cx = CX;
NBCGenTryPost(stree, onexception_list, &onexcpsp);
NBCGenTryPost(stree, onexception_list, &onexcpsp, ret);

// onexception の終了
NBCOnexcpBackPatchs(onexcp_cx, CX); // onexception の終了をバックパッチ
NBCOnexcpBackPatchs(onexcp_cx); // onexception の終了をバックパッチ
NBCGenCode(kNBCPopHandlers, 0);

// ONEXCPSP を戻す
Expand Down Expand Up @@ -2278,13 +2302,11 @@ void NBCGenSyntaxCode(nps_syntax_node_t * stree, nps_syntax_node_t * node, bool
break;

case kNPSTry:
NBCGenTry(stree, node->op1, node->op2);
NVCGenNoResult(ret);
NBCGenTry(stree, node->op1, node->op2, ret);
break;

case kNPSIf:
NBCGenIfThenElse(stree, node->op1, node->op2, ret);
// NVCGenNoResult(ret);
break;

case kNPSLoop:
Expand Down
60 changes: 54 additions & 6 deletions src/newt_core/NewtFns.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,14 @@ newtRef NcLookupSymbol(newtRefArg r, newtRefArg name)
* @param name [in] 例外シンボル
* @param data [in] 例外データ
*
* @retval NIL
* @retval stack head (to be pushed by calling function)
*
* @note スクリプトからの呼出し用
*/

newtRef NsThrow(newtRefArg rcvr, newtRefArg name, newtRefArg data)
{
NVMThrow(name, data);
return kNewtRefNIL;
return NVMThrow(name, data);
}


Expand All @@ -220,15 +219,14 @@ newtRef NsThrow(newtRefArg rcvr, newtRefArg name, newtRefArg data)
*
* @param rcvr [in] レシーバ
*
* @retval NIL
* @retval stack head (to be pushed by calling function)
*
* @note スクリプトからの呼出し用
*/

newtRef NsRethrow(newtRefArg rcvr)
{
NVMRethrow();
return kNewtRefNIL;
return NVMRethrow();
}


Expand Down Expand Up @@ -2201,6 +2199,56 @@ newtRef NsGetEnv(newtRefArg rcvr, newtRefArg r)
#if 0
#pragma mark -
#endif

/*------------------------------------------------------------------------*/
/**
* Determine if two binaries are equal.
*
* @param rcvr self (ignored).
* @param a the first binary to consider.
* @param b the second binary to consider.
* @return true if the two binaries are equal, nil otherwise.
*/
newtRef NsBinEqual(newtRefArg rcvr, newtRefArg a, newtRefArg b)
{
char* aString;
char* bString;
newtRefVar theResult = kNewtRefNIL;

(void) rcvr;

/* check parameters */
if (! NewtRefIsBinary(a))
{
theResult = NewtThrow(kNErrNotABinaryObject, a);
} else if (! NewtRefIsBinary(b)) {
theResult = NewtThrow(kNErrNotABinaryObject, b);
} else if (a == b) {
theResult = kNewtRefTRUE;
} else {
size_t aLen = NewtBinaryLength(a);
size_t bLen = NewtBinaryLength(b);

if (aLen == bLen) {
newtObjRef aObj = NewtRefToPointer(a);
newtObjRef bObj = NewtRefToPointer(b);
newtRefVar aKlass = aObj->as.klass;
newtRefVar bKlass = bObj->as.klass;

if (NewtRefEqual(aKlass, bKlass)) {
uint8_t* aPtr = NewtRefToBinary(a);
uint8_t* bPtr = NewtRefToBinary(b);

if (memcmp(aPtr, bPtr, aLen) == 0) {
theResult = kNewtRefTRUE;
}
}
}
}

return theResult;
}

/*------------------------------------------------------------------------*/
/** オフセット位置から符号付きの1バイトを取り出す。
*
Expand Down
4 changes: 1 addition & 3 deletions src/newt_core/NewtGC.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,20 +465,18 @@ void NewtGCStackMark(vm_env_t * env, bool mark)
// 例外ハンドラ・スタック
NewtGCRefMark(env->currexcp, mark);

/*
{
vm_excp_t * excpstack;
vm_excp_t * excp;

excpstack = (vm_excp_t *)env->excpstack.stackp;

for (i = 0; i < env->excpsp; i++)
for (i = 0; i < env->excpstack.sp; i++)
{
excp = &excpstack[i];
NewtGCRefMark(excp->sym, mark);
}
}
*/
}


Expand Down
Loading

0 comments on commit b80abc3

Please sign in to comment.