constant_t*constant;
for_start_t for_start;
abc_exception_t *exception;
- abc_exception_list_t *exception_list;
regexp_t regexp;
+ struct {
+ abc_exception_list_t *l;
+ code_t*finally;
+ } catch_list;
}
%token<token> KW_NEW "new"
%token<token> KW_NATIVE "native"
%token<token> KW_FUNCTION "function"
+%token<token> KW_FINALLY "finally"
%token<token> KW_UNDEFINED "undefined"
%token<token> KW_CONTINUE "continue"
%token<token> KW_CLASS "class"
%type <code> PACKAGE_DECLARATION SLOT_DECLARATION
%type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
%type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
-%type <exception> CATCH
-%type <exception_list> CATCH_LIST
+%type <exception> CATCH FINALLY
+%type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
%type <code> CLASS_DECLARATION
%type <code> NAMESPACE_DECLARATION
%type <code> INTERFACE_DECLARATION
static int yyerror(char*s)
{
syntaxerror("%s", s);
+ return 0; //make gcc happy
}
static char* concat2(const char* t1, const char* t2)
{
new_state();
state->package = filename;
+ // needed for state->method->late_binding:
+ state->method = rfx_calloc(sizeof(methodstate_t));
}
void finish_file()
{
c = code_append(c, header);
c = code_append(c, var_block(body));
/* append return if necessary */
- if(!c || c->opcode != OPCODE_RETURNVOID &&
- c->opcode != OPCODE_RETURNVALUE) {
+ if(!c || (c->opcode != OPCODE_RETURNVOID &&
+ c->opcode != OPCODE_RETURNVALUE)) {
c = abc_returnvoid(c);
}
return c;
if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
return c;
syntaxerror("can't convert type %s to %s", from->name, to->name);
+ return 0; // make gcc happy
}
code_t*defaultvalue(code_t*c, classinfo_t*type)
else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
+ return 0;
}
static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
} else if(r->opcode == OPCODE_GETLOCAL_3) {
write->opcode = OPCODE_SETLOCAL_3;
} else {
- code_dump(r, 0, 0, "", stdout);
+ code_dump(r);
syntaxerror("illegal lvalue: can't assign a value to this expression");
}
code_t* c = 0;
c = abc_kill(c, temp);
}
}
+ return c;
+}
+
+char is_break_or_jump(code_t*c)
+{
+ if(!c)
+ return 0;
+ if(c->opcode == OPCODE_JUMP ||
+ c->opcode == OPCODE___BREAK__ ||
+ c->opcode == OPCODE___CONTINUE__ ||
+ c->opcode == OPCODE_THROW ||
+ c->opcode == OPCODE_RETURNVOID ||
+ c->opcode == OPCODE_RETURNVALUE) {
+ return 1;
+ }
+ return 0;
+}
+
+
+#define IS_FINALLY_TARGET(op) \
+ ((op) == OPCODE___CONTINUE__ || \
+ (op) == OPCODE___BREAK__ || \
+ (op) == OPCODE_RETURNVOID || \
+ (op) == OPCODE_RETURNVALUE || \
+ (op) == OPCODE___RETHROW__)
+
+static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
+{
+#define NEED_EXTRA_STACK_ARG
+ code_t*finally_label = abc_nop(0);
+ NEW(lookupswitch_t, l);
+ //_lookupswitch
+
+ code_t*i = c;
+ int count=0;
+ while(i) {
+ code_t*prev = i->prev;
+ if(IS_FINALLY_TARGET(i->opcode)) {
+ code_t*p = prev;
+ char needvalue=0;
+ if(i->opcode == OPCODE___RETHROW__ ||
+ i->opcode == OPCODE_RETURNVALUE) {
+ if(i->opcode == OPCODE___RETHROW__)
+ i->opcode = OPCODE_THROW;
+ needvalue=1;
+ p = abc_coerce_a(p);
+ p = abc_setlocal(p, tempvar);
+ }
+ p = abc_pushbyte(p, count++);
+ p = abc_jump(p, finally_label);
+ code_t*target = p = abc_label(p);
+#ifdef NEED_EXTRA_STACK_ARG
+ p = abc_pop(p);
+#endif
+ if(needvalue) {
+ p = abc_getlocal(p, tempvar);
+ }
+
+ p->next = i;i->prev = p;
+ list_append(l->targets, target);
+ }
+ i = prev;
+ }
+
+ code_t*j,*f;
+ c = abc_pushbyte(c, -1);
+ c = code_append(c, finally_label);
+ c = code_append(c, finally);
+
+#ifdef NEED_EXTRA_STACK_ARG
+ c = abc_dup(c);
+#endif
+ c = abc_lookupswitch(c, l);
+ c = l->def = abc_label(c);
+#ifdef NEED_EXTRA_STACK_ARG
+ c = abc_pop(c);
+#endif
return c;
}
+static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
+{
+ code_t*i = c;
+ while(i) {
+ code_t*prev = i->prev;
+ if(IS_FINALLY_TARGET(i->opcode)) {
+ if(i->opcode == OPCODE___RETHROW__)
+ i->opcode = OPCODE_THROW;
+ code_t*end = code_dup(finally);
+ code_t*start = code_start(end);
+ if(prev) prev->next = start;
+ start->prev = prev;
+ i->prev = end;
+ end->next = i;
+ }
+ i = prev;
+ }
+ return code_append(c, finally);
+}
+
+code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
+{
+ if(!finally)
+ return c;
+ code_t*i = c;
+ char cantdup=0;
+ int num_insertion_points=0;
+ while(i) {
+ if(IS_FINALLY_TARGET(i->opcode))
+ num_insertion_points++;
+ i = i->prev;
+ }
+ i = finally;
+ int code_size=0;
+ while(i) {
+ code_size++;
+ if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
+ cantdup=1;
+ }
+ i = i->prev;
+ }
+ int simple_version_cost = (1+num_insertion_points)*code_size;
+ int lookup_version_cost = 4*num_insertion_points + 5;
+
+ if(cantdup || simple_version_cost > lookup_version_cost) {
+ printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
+ return insert_finally_lookup(c, finally, tempvar);
+ } else {
+ printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
+ return insert_finally_simple(c, finally, tempvar);
+ }
+}
%}
/* ------------ try / catch /finally ---------------- */
-FINALLY: "finally" '{' CODE '}'
-MAYBE_FINALLY: | FINALLY
-
CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
- '{' CODE '}' {
+ '{' MAYBECODE '}' {
namespace_t name_ns = {ACCESS_PACKAGE, ""};
multiname_t name = {QNAME, &name_ns, 0, $3};
code_t*c = 0;
int i = find_variable_safe($3)->index;
- e->target = c = abc_setlocal(0, i);
+ e->target = c = abc_nop(0);
+ c = abc_setlocal(c, i);
c = code_append(c, $8);
c = abc_kill(c, i);
c = var_block(c);
old_state();
-
+}
+FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
+ $4 = var_block($4);
+ if(!$4) {
+ $$=0;
+ old_state();
+ } else {
+ NEW(abc_exception_t, e)
+ e->exc_type = 0; //all exceptions
+ e->var_name = 0; //no name
+ e->target = 0;
+ e->to = abc_nop(0);
+ e->to = code_append(e->to, $4);
+ old_state();
+ $$ = e;
+ }
}
-CATCH_LIST: CATCH {$$=list_new();list_append($$,$1);}
-CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$,$2);}
-
-TRY : "try" '{' {new_state();} CODE '}' CATCH_LIST MAYBE_FINALLY {
- code_t*start = code_start($4);
- $$=$4;
+CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
+CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
+CATCH_FINALLY_LIST: CATCH_LIST {$$=$1};
+CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
+ $$ = $1;
+ $$.finally = 0;
+ if($2) {
+ list_append($$.l,$2);
+ $$.finally = $2->to;$2->to=0;
+ }
+}
+CATCH_FINALLY_LIST: FINALLY {
+ $$.l=list_new();
+ $$.finally = 0;
+ if($1) {
+ list_append($$.l,$1);
+ $$.finally = $1->to;$1->to=0;
+ }
+}
+TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
code_t*out = abc_nop(0);
- code_t*jmp = $$ = abc_jump($$, out);
- abc_exception_list_t*l = $6;
+ code_t*start = abc_nop(0);
+ $$ = code_append(start, $4);
+ if(!is_break_or_jump($4)) {
+ $$ = abc_jump($$, out);
+ }
+ code_t*end = $$ = abc_nop($$);
+
+ int tmp;
+ if($6.finally)
+ tmp = new_variable("__finally__", 0, 0);
+
+ abc_exception_list_t*l = $6.l;
+ int count=0;
while(l) {
abc_exception_t*e = l->abc_exception;
+ if(e->var_name) {
+ $$ = code_append($$, e->target);
+ $$ = abc_jump($$, out);
+ } else {
+ parserassert((ptroff_t)$6.finally);
+ // finally block
+ e->target = $$ = abc_nop($$);
+ $$ = abc___rethrow__($$);
+ }
+
e->from = start;
- e->to = jmp;
- $$ = code_append($$, e->target);
- $$ = abc_jump($$, out);
+ e->to = end;
+
l = l->next;
}
$$ = code_append($$, out);
- jmp->branch = out;
+
+ $$ = insert_finally($$, $6.finally, tmp);
- list_concat(state->method->exceptions, $6);
+ list_concat(state->method->exceptions, $6.l);
$$ = var_block($$);
old_state();
E : E "++" { code_t*c = 0;
classinfo_t*type = $1.t;
- if(is_getlocal($1.c) && TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t)) {
+ if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
int nr = getlocalnr($1.c);
code_free($1.c);$1.c=0;
if(TYPE_IS_INT($1.t)) {