58ebc4f77e
* BUG: Breakpoints are now safely ignored. git-svn-id: svn://localhost/gambas/trunk@4875 867c0c6c-44f3-4631-809d-bfa615b0a4ec
1058 lines
29 KiB
C++
1058 lines
29 KiB
C++
/***************************************************************************
|
|
|
|
jit_read.c
|
|
|
|
gb.jit component
|
|
|
|
(c) 2012 Emil Lenngren <emil.lenngren [at] gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 1, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __JIT_READ_C
|
|
|
|
#include "jit.h"
|
|
#include "gb_pcode.h"
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
static unsigned short* code;
|
|
static int size;
|
|
static int pos;
|
|
static int addr;
|
|
static uint stack_start;
|
|
|
|
static bool in_dup = false;
|
|
|
|
unsigned short* get_current_read_pos(){
|
|
return code + pos;
|
|
}
|
|
|
|
static std::vector<Expression*> _stack;
|
|
|
|
void ref_stack(){
|
|
for(size_t i=0, e=_stack.size(); i!=e; i++)
|
|
_stack[i]->ref_on_stack();
|
|
}
|
|
|
|
static int stack_size(){
|
|
return _stack.size();
|
|
}
|
|
|
|
static bool stack_empty(){
|
|
return _stack.size() == stack_start;
|
|
}
|
|
|
|
static void push(Expression* expr){
|
|
_stack.push_back(expr);
|
|
}
|
|
|
|
static Expression*& top(){
|
|
return _stack.back();
|
|
}
|
|
|
|
static Expression* pop(){
|
|
Expression* val = _stack.back();
|
|
_stack.pop_back();
|
|
return val;
|
|
}
|
|
|
|
static Expression** extract(int num){
|
|
size_t s = _stack.size();
|
|
size_t start = (int)s-num;
|
|
Expression** it = &_stack[start];
|
|
_stack.resize(start);
|
|
return it;
|
|
}
|
|
|
|
static Expression** extract_reverse(int num){
|
|
Expression** it = extract(num);
|
|
std::reverse(it, it+num);
|
|
return it;
|
|
}
|
|
|
|
std::vector<int> taken_addresses;
|
|
|
|
void mark_address_taken(int addr){
|
|
taken_addresses.push_back(addr);
|
|
}
|
|
|
|
static void push_statement(Expression* expr){
|
|
Statement* s = new Statement();
|
|
s->addr = addr;
|
|
s->address_taken = false;
|
|
s->expr = expr;
|
|
all_statements.push_back(s);
|
|
}
|
|
|
|
static Expression*& top_statement(){
|
|
return all_statements.back()->expr;
|
|
}
|
|
|
|
static Expression* pop_statement(){
|
|
Expression* ret = top_statement();
|
|
delete all_statements.back();
|
|
all_statements.pop_back();
|
|
return ret;
|
|
}
|
|
|
|
static void push_constant(CLASS_DESC* desc){
|
|
if (TYPE_is_string(desc->constant.type) && desc->constant.translate){
|
|
char* addr = (char *)GB.Translate(desc->constant.value._string);
|
|
push(new PushCStringExpression(addr, 0, strlen(addr)));
|
|
return;
|
|
}
|
|
void* value = &desc->constant.value;
|
|
switch(desc->constant.type){
|
|
case T_BOOLEAN:
|
|
push(new PushIntegerExpression(1, *(bool*)value)); return;
|
|
case T_BYTE:
|
|
push(new PushIntegerExpression(8, *(unsigned char*)value)); return;
|
|
case T_SHORT:
|
|
push(new PushIntegerExpression(16, *(short*)value)); return;
|
|
case T_INTEGER:
|
|
push(new PushIntegerExpression(32, *(int*)value)); return;
|
|
case T_LONG:
|
|
push(new PushIntegerExpression(64, *(int64_t*)value)); return;
|
|
case T_SINGLE:
|
|
push(new PushFloatExpression(32, *(float*)value)); return;
|
|
case T_FLOAT:
|
|
push(new PushFloatExpression(64, *(double*)value)); return;
|
|
case T_CSTRING:
|
|
push(new PushCStringExpression(*(char**)value, 0, strlen(*(char**)value))); return;
|
|
default:
|
|
assert(false && "Illegal constant type");
|
|
}
|
|
}
|
|
|
|
static int get_subr_nargs(int digit, int extra){
|
|
int nargs = extra;
|
|
switch(digit){
|
|
case 0x6D: //Timer
|
|
case 0x6E: //Now
|
|
case 0x75: //Error
|
|
case 0x76: //Debug
|
|
nargs = 0; break;
|
|
|
|
case 0x43: //Len
|
|
case 0x44: //Space
|
|
case 0x46: //Trim
|
|
case 0x47: //UCase
|
|
case 0x48: //LCase
|
|
case 0x49: //Chr
|
|
case 0x53: //DConv
|
|
case 0x54: //Abs
|
|
case 0x55: //Int
|
|
case 0x56: //Fix
|
|
case 0x57: //Sgn
|
|
case 0x58: //Math
|
|
case 0x63: //IsAscii, IsLetter ...
|
|
case 0x65: //IsBoolean ...
|
|
case 0x66: //TypeOf
|
|
case 0x67: //CBool ...
|
|
case 0x6A: //Val
|
|
case 0x6B: //Str
|
|
case 0x6F: //Year
|
|
case 0x79: //Close
|
|
case 0x7B: //Line Input
|
|
case 0x7F: //Flush
|
|
case 0x80: //Lock
|
|
case 0x81: //Input From, Output To, Error To
|
|
case 0x85: //Kill
|
|
case 0x86: //Mkdir, deprecated -> Even() & Odd()
|
|
case 0x87: //Rmdir
|
|
case 0x8E: //DFree
|
|
case 0x90: //IsDir
|
|
case 0x95: //Free
|
|
case 0x98: //Sleep
|
|
case 0x99: //VarPtr
|
|
case 0x9B: //Tr
|
|
case 0x9C: //Quote, Shell, Html
|
|
case 0x9D: //Unquote
|
|
case 0x9E: //MkInteger, ...
|
|
case 0x9F: //Bool@, Byte@, ...
|
|
nargs = 1; break;
|
|
|
|
case 0x45: //String
|
|
case 0x50: //Scan
|
|
case 0x5D: //Min
|
|
case 0x5E: //Max
|
|
case 0x62: //Math2
|
|
case 0x64: //Bit
|
|
case 0x78: //Open
|
|
case 0x7D: //Read
|
|
case 0x88: //Move
|
|
case 0x89: //Copy
|
|
nargs = 2; break;
|
|
|
|
case 0x8A: //Link, deprecated -> IsNan() & IsInf()
|
|
nargs = extra == 0 ? 2 : 1; break;
|
|
|
|
case 0x52: //Conv
|
|
case 0x5F: //IIf
|
|
case 0x73: //DateAdd, DateDiff
|
|
case 0x7E: //Write
|
|
nargs = 3; break;
|
|
|
|
case 0x93: //Exec
|
|
nargs = 4; break;
|
|
}
|
|
return nargs;
|
|
}
|
|
|
|
static void JIT_push_unknown(){
|
|
const char* name = CP->load->unknown[code[pos+1]];
|
|
pos += 2;
|
|
bool defined;
|
|
|
|
if (TYPE_is_pure_object(top()->type)){
|
|
//These can now be on top of the stack:
|
|
//T_NULL, a null object of type top()->type
|
|
//A pure object (type > T_OBJECT) with type top()->type
|
|
//When top()->type is a virtual class:
|
|
// T_NULL can be returned (only by non-static methods at the moment)
|
|
// An object represented as a virtual class top()->type is the virtual class,
|
|
// but the real type can be something else.
|
|
// A T_CLASS of the class top()->type:
|
|
// happens when a native static method says it returns a virtual object
|
|
// But beware, a native static method might as well return a virtual object
|
|
// of type .SubCollection
|
|
defined = true;
|
|
CLASS* klass = (CLASS*)(void*)top()->type;
|
|
|
|
JIT_load_class(klass);
|
|
|
|
int index = CLASS_find_symbol(klass, name);
|
|
|
|
if (index == NO_SYMBOL){
|
|
if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL)
|
|
THROW(E_NSYMBOL, name, klass->name);
|
|
|
|
if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){
|
|
//FIXME
|
|
abort();
|
|
}
|
|
|
|
//Goto unknown method FIXME
|
|
abort();
|
|
}
|
|
|
|
CLASS_DESC* desc = klass->table[index].desc;
|
|
|
|
switch (CLASS_DESC_get_type(desc)){
|
|
case CD_CONSTANT:
|
|
if (klass->is_virtual){
|
|
//FIXME OK to extract the value and ignore actually evaluating, and what if we got a null value..???
|
|
/*delete*/ pop();
|
|
push_constant(desc);
|
|
return;
|
|
}
|
|
push(new PushPureObjectConstantExpression(pop(), index));
|
|
return;
|
|
case CD_VARIABLE:
|
|
/*int offset = desc->variable.offset;
|
|
TYPE type = desc->variable.ctype;*/
|
|
push(new PushPureObjectVariableExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_STATIC_VARIABLE:
|
|
THROW(E_STATIC, klass->name, name);
|
|
//push(new PushPureObjectStaticVariableExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_PROPERTY:
|
|
case CD_PROPERTY_READ:
|
|
if (klass->is_virtual)
|
|
push(new PushVirtualPropertyExpression(pop(), index));
|
|
else
|
|
push(new PushPureObjectPropertyExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_STRUCT_FIELD:
|
|
push(new PushPureObjectStructFieldExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_STATIC_PROPERTY:
|
|
case CD_STATIC_PROPERTY_READ:
|
|
//See System.User why this is a problem ...
|
|
//Runtime check is now done.
|
|
//if (!klass->is_virtual)
|
|
// THROW(E_STATIC, klass->name, name);
|
|
//push(new PushVirtualClassStaticProperty(pop(), index));
|
|
push(new PushPureObjectStaticPropertyExpression(pop(), index, name));
|
|
return;
|
|
|
|
case CD_METHOD:
|
|
if (klass->is_virtual)
|
|
push(new PushVirtualFunctionExpression(pop(), index));
|
|
else
|
|
push(new PushPureObjectFunctionExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_STATIC_METHOD:
|
|
if (klass->is_virtual)
|
|
push(new PushVirtualStaticFunctionExpression(pop(), index));
|
|
else
|
|
push(new PushPureObjectStaticFunctionExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_EXTERN:
|
|
push(new PushExternExpression(klass, index, pop()));
|
|
return;
|
|
|
|
default: THROW(E_NSYMBOL, name, klass->name);
|
|
}
|
|
|
|
} else if (top()->type == T_OBJECT){
|
|
defined = false;
|
|
push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2));
|
|
} else if (top()->type == T_CLASS){
|
|
defined = true;
|
|
|
|
PushClassExpression* pce = dyn_cast<PushClassExpression>(top());
|
|
assert(pce);
|
|
|
|
CLASS* klass = pce->klass;
|
|
|
|
int index = CLASS_find_symbol(klass, name);
|
|
|
|
if (index == NO_SYMBOL){
|
|
if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL)
|
|
THROW(E_NSYMBOL, name, klass->name);
|
|
|
|
if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){
|
|
//FIXME
|
|
abort();
|
|
}
|
|
|
|
//Goto unknown method FIXME
|
|
abort();
|
|
}
|
|
|
|
CLASS_DESC* desc = klass->table[index].desc;
|
|
|
|
switch (CLASS_DESC_get_type(desc)){
|
|
case CD_CONSTANT: {
|
|
/*delete*/ pop();
|
|
push_constant(desc);
|
|
return;
|
|
}
|
|
case CD_VARIABLE:
|
|
if (!klass->auto_create)
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
/*delete*/ pop();
|
|
push(new PushPureObjectVariableExpression(new PushAutoCreateExpression(klass), index));
|
|
return;
|
|
|
|
case CD_STRUCT_FIELD:
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
|
|
case CD_STATIC_VARIABLE:
|
|
/*delete*/ pop();
|
|
push(new PushStaticVariableExpression(&desc->variable));
|
|
return;
|
|
|
|
case CD_PROPERTY:
|
|
case CD_PROPERTY_READ:
|
|
if (!klass->auto_create)
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
/*delete*/ pop();
|
|
push(new PushPureObjectPropertyExpression(new PushAutoCreateExpression(klass), index));
|
|
return;
|
|
|
|
case CD_STATIC_PROPERTY:
|
|
case CD_STATIC_PROPERTY_READ:
|
|
push(new PushStaticPropertyExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_METHOD:
|
|
if (!klass->auto_create)
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
/*delete*/ pop();
|
|
push(new PushPureObjectFunctionExpression(new PushAutoCreateExpression(klass), index));
|
|
return;
|
|
|
|
case CD_STATIC_METHOD:
|
|
/*int kind = FUNCTION_PUBLIC;
|
|
if (FUNCTION_is_native(&desc->method)){
|
|
if (desc->method.subr)
|
|
kind = FUNCTION_SUBR;
|
|
else
|
|
kind = FUNCTION_NATIVE;
|
|
}*/
|
|
push(new PushStaticFunctionExpression(pop(), index));
|
|
return;
|
|
|
|
case CD_EXTERN:
|
|
pop();
|
|
push(new PushExternExpression(klass, desc->ext.exec, NULL));
|
|
return;
|
|
|
|
default: THROW(E_NSYMBOL, name, klass->name);
|
|
|
|
}
|
|
|
|
} else if (top()->type == T_FUNCTION){
|
|
assert(false && "Syntax error");
|
|
} else if (top()->type == T_VARIANT){
|
|
defined = false;
|
|
push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2));
|
|
} else if (top()->type == T_NULL) {
|
|
THROW(E_NULL);
|
|
} else {
|
|
THROW(E_NOBJECT);
|
|
}
|
|
}
|
|
|
|
static void JIT_pop_unknown(){
|
|
const char* name = CP->load->unknown[code[pos+1]];
|
|
pos += 2;
|
|
|
|
if (TYPE_is_pure_object(top()->type)){
|
|
//These can now be on top of the stack:
|
|
//T_NULL, a null object of type top()->type
|
|
//A pure object (type > T_OBJECT) with type top()->type
|
|
//When top()->type is a virtual class:
|
|
// T_NULL can be returned (only by non-static methods at the moment)
|
|
// An object represented as a virtual class top()->type is the virtual class,
|
|
// but the real type can be something else.
|
|
// A T_CLASS of the class top()->type:
|
|
// happens when a native static method says it returns a virtual object
|
|
// But beware, a native static method might as well return a virtual object
|
|
// of type .SubCollection
|
|
// By the time of this writing, there are no write properties taking virtual classes.
|
|
CLASS* klass = (CLASS*)(void*)top()->type;
|
|
|
|
int index = CLASS_find_symbol(klass, name);
|
|
|
|
if (index == NO_SYMBOL){
|
|
if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL)
|
|
THROW(E_NSYMBOL, name, klass->name);
|
|
|
|
//Goto unknown property FIXME
|
|
abort();
|
|
}
|
|
|
|
CLASS_DESC* desc = klass->table[index].desc;
|
|
|
|
Expression* obj = pop();
|
|
Expression* val = pop();
|
|
|
|
switch(CLASS_DESC_get_type(desc)){
|
|
case CD_CONSTANT:
|
|
THROW(E_NPROPERTY, klass->name, name);
|
|
|
|
case CD_VARIABLE:
|
|
push_statement(new PopPureObjectVariableExpression(obj, val, index));
|
|
return;
|
|
|
|
case CD_STRUCT_FIELD:
|
|
if (desc->variable.ctype.id == TC_STRUCT || desc->variable.ctype.id == TC_ARRAY)
|
|
THROW(E_NWRITE, klass->name, name);
|
|
push_statement(new PopPureObjectStructFieldExpression(obj, val, index));
|
|
return;
|
|
|
|
case CD_STATIC_VARIABLE:
|
|
THROW(E_STATIC, klass->name, name);
|
|
//push_statement(new PopPureObjectStaticVariableExpression(obj, val, index));
|
|
return;
|
|
|
|
case CD_PROPERTY:
|
|
if (klass->is_virtual)
|
|
push_statement(new PopVirtualPropertyExpression(obj, val, index, name, false));
|
|
else
|
|
push_statement(new PopPureObjectPropertyExpression(obj, val, index));
|
|
return;
|
|
|
|
case CD_STATIC_PROPERTY:
|
|
//Should only be used on virtual classes. Runtime check is added if not, throwing E_STATIC
|
|
//THROW(E_STATIC, klass->name, name);
|
|
push_statement(new PopVirtualPropertyExpression(obj, val, index, name, true));
|
|
return;
|
|
|
|
case CD_PROPERTY_READ:
|
|
case CD_STATIC_PROPERTY_READ:
|
|
THROW(E_NWRITE, klass->name, name);
|
|
|
|
case CD_METHOD:
|
|
case CD_STATIC_METHOD:
|
|
THROW(E_NPROPERTY, klass->name, name);
|
|
|
|
default: THROW(E_NSYMBOL, name, klass->name);
|
|
}
|
|
} else if (top()->type == T_OBJECT){
|
|
Expression* obj = pop();
|
|
Expression* val = pop();
|
|
push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2));
|
|
} else if (top()->type == T_CLASS){
|
|
|
|
PushClassExpression* pce = dyn_cast<PushClassExpression>(top());
|
|
assert(pce);
|
|
|
|
CLASS* klass = pce->klass;
|
|
|
|
int index = CLASS_find_symbol(klass, name);
|
|
|
|
if (index == NO_SYMBOL){
|
|
if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL)
|
|
THROW(E_NSYMBOL, name, klass->name);
|
|
|
|
//Goto unknown property FIXME
|
|
abort();
|
|
}
|
|
|
|
CLASS_DESC* desc = klass->table[index].desc;
|
|
|
|
Expression* obj = pop();
|
|
Expression* val = pop();
|
|
|
|
switch(CLASS_DESC_get_type(desc)){
|
|
case CD_CONSTANT:
|
|
THROW(E_NPROPERTY, klass->name, name);
|
|
|
|
case CD_VARIABLE:
|
|
if (!klass->auto_create)
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
//delete obj;
|
|
push_statement(new PopPureObjectVariableExpression(new PushAutoCreateExpression(klass), val, index));
|
|
return;
|
|
|
|
case CD_STRUCT_FIELD:
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
|
|
case CD_STATIC_VARIABLE:
|
|
//delete obj;
|
|
if (desc->variable.ctype.id == TC_ARRAY || desc->variable.ctype.id == TC_STRUCT)
|
|
THROW_ILLEGAL();
|
|
push_statement(new PopStaticVariableExpression(desc->variable.type, (char*)desc->variable.klass->stat + desc->variable.offset, val));
|
|
return;
|
|
|
|
case CD_PROPERTY:
|
|
if (!klass->auto_create)
|
|
THROW(E_DYNAMIC, klass->name, name);
|
|
//delete obj;
|
|
push_statement(new PopPureObjectPropertyExpression(new PushAutoCreateExpression(klass), val, index));
|
|
return;
|
|
|
|
case CD_STATIC_PROPERTY:
|
|
//delete obj;
|
|
push_statement(new PopStaticPropertyExpression(klass, val, index));
|
|
return;
|
|
|
|
case CD_PROPERTY_READ:
|
|
case CD_STATIC_PROPERTY_READ:
|
|
THROW(E_NWRITE, klass->name, name);
|
|
|
|
case CD_METHOD:
|
|
case CD_STATIC_METHOD:
|
|
THROW(E_NPROPERTY, klass->name, name);
|
|
|
|
default: THROW(E_NSYMBOL, name, klass->name);
|
|
}
|
|
} else if (top()->type == T_FUNCTION){
|
|
assert(false && "syntax error");
|
|
} else if (top()->type == T_VARIANT){
|
|
Expression* obj = pop();
|
|
Expression* val = pop();
|
|
push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2));
|
|
return;
|
|
} else if (top()->type == T_NULL) {
|
|
THROW(E_NULL);
|
|
} else {
|
|
THROW(E_NOBJECT);
|
|
}
|
|
}
|
|
|
|
static void check_swap(){
|
|
if (!in_dup && !stack_empty()){
|
|
/* Then this have happened:
|
|
Push A
|
|
Push B
|
|
Pop A
|
|
Pop B
|
|
and we have just made the Pop A
|
|
So we replace Push A, Push B, Pop A to a SwapExpression,
|
|
that is taken as input for Pop B */
|
|
push(new SwapExpression(pop(), pop_statement()));
|
|
}
|
|
}
|
|
|
|
#define NEXT pos+=ncode; goto __NEXT;
|
|
#define CONT goto __NEXT;
|
|
|
|
static void JIT_read_statement(){
|
|
while(pos < size){
|
|
unsigned short op = code[pos];
|
|
int ncode;
|
|
switch (op & 0xFF00){
|
|
case C_PUSH_UNKNOWN: case C_POP_UNKNOWN:
|
|
case C_PUSH_INTEGER:
|
|
case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: case C_GOSUB:
|
|
case C_NEXT: case C_JUMP_NEXT:
|
|
case C_TRY:
|
|
|
|
ncode = 2;
|
|
break;
|
|
|
|
case C_PUSH_LONG:
|
|
|
|
ncode = 3;
|
|
break;
|
|
|
|
case C_BYREF:
|
|
ncode = 2 + (op & 0xFF);
|
|
break;
|
|
|
|
default:
|
|
|
|
if ((op & 0xFF00) == (C_PUSH_CONST | 0xF00))
|
|
ncode = 2;
|
|
else
|
|
ncode = 1;
|
|
}
|
|
|
|
unsigned short digit = (op >> 12U);
|
|
int value = op & 0xFFF;
|
|
if (value >= 0x800) value |= 0xFFFFF000;
|
|
|
|
switch (digit){
|
|
case 0xF:
|
|
push(new PushIntegerExpression(32, value));
|
|
NEXT
|
|
case 0xE: {
|
|
VALUE val;
|
|
unsigned short ind = op & 0xFFF;
|
|
if ((op & 0xF00) == 0xF00)
|
|
ind = code[++pos];
|
|
#define LOCAL_gettext GB.Translate
|
|
VALUE_class_constant_inline(CP, &val, ind);
|
|
#undef LOCAL_gettext
|
|
|
|
switch(val.type){
|
|
case T_INTEGER:
|
|
push(new PushIntegerExpression(32, val._integer.value));
|
|
NEXT
|
|
case T_LONG:
|
|
push(new PushIntegerExpression(64, val._long.value));
|
|
NEXT
|
|
case T_SINGLE:
|
|
push(new PushFloatExpression(32, val._single.value));
|
|
NEXT
|
|
case T_FLOAT:
|
|
push(new PushFloatExpression(64, val._float.value));
|
|
NEXT
|
|
case T_CSTRING:
|
|
push(new PushCStringExpression(val._string.addr, val._string.start, val._string.len));
|
|
NEXT
|
|
case T_POINTER:
|
|
push(new PushNullPointerExpression());
|
|
NEXT
|
|
}
|
|
assert(false && "Invalid constant type");
|
|
}
|
|
case 0xD:
|
|
if (value & 0x800)
|
|
push_statement(new PopStaticExpression(pop(), value & 0x7FF));
|
|
else
|
|
push_statement(new PopDynamicExpression(pop(), value & 0x7FF));
|
|
check_swap();
|
|
NEXT
|
|
case 0xC:
|
|
if (value & 0x800)
|
|
push(new PushStaticExpression(value & 0x7FF));
|
|
else
|
|
push(new PushDynamicExpression(value & 0x7FF));
|
|
NEXT
|
|
case 0xB:
|
|
if (value & 0x800)
|
|
push(new PushFunctionExpression(value & 0x7FF));
|
|
else
|
|
push(new PushClassExpression(value & 0x7FF));
|
|
NEXT
|
|
case 0xA:
|
|
push(new AddQuickExpression(pop(), value));
|
|
NEXT
|
|
|
|
default: {
|
|
digit = op & 0xFF00;
|
|
value = op & 0xFF;
|
|
if (value >= 0x80) value |= 0xFFFFFF00;
|
|
|
|
switch(digit){
|
|
case C_PUSH_LOCAL:
|
|
if (value >= FP->n_local && get_ctrl_type(value) == T_CLASS){
|
|
//Control variable
|
|
push(new PushClassExpression(get_ctrl_class(value)));
|
|
} else {
|
|
push(new PushLocalExpression(value));
|
|
}
|
|
NEXT
|
|
case C_PUSH_PARAM:
|
|
push(new PushParamExpression(value));
|
|
NEXT
|
|
case C_POP_LOCAL:
|
|
push_statement(new PopLocalExpression(pop(), value));
|
|
check_swap();
|
|
NEXT
|
|
case C_POP_PARAM:
|
|
push_statement(new PopParamExpression(pop(), value));
|
|
check_swap();
|
|
NEXT
|
|
case C_PUSH_UNKNOWN:
|
|
JIT_push_unknown();
|
|
CONT
|
|
case C_POP_UNKNOWN:
|
|
JIT_pop_unknown();
|
|
check_swap();
|
|
CONT
|
|
case C_POP_CTRL:
|
|
in_dup = false; //From Dup
|
|
push_statement(new PopCtrlExpression(pop(), value));
|
|
NEXT
|
|
case C_POP_OPTIONAL:
|
|
push_statement(new PopOptionalExpression(pop(), value));
|
|
NEXT
|
|
case C_PUSH_EXTERN:
|
|
push(new PushExternExpression(CP, value, NULL));
|
|
NEXT
|
|
case C_PUSH_EVENT:
|
|
push(new PushEventExpression(value));
|
|
NEXT
|
|
case C_PUSH_ARRAY:
|
|
push(new PushArrayExpression(extract(value), value));
|
|
NEXT
|
|
case C_POP_ARRAY: {
|
|
Expression** it = extract(value);
|
|
Expression* val = pop();
|
|
push_statement(new PopArrayExpression(it, value, val));
|
|
check_swap();
|
|
NEXT
|
|
}
|
|
case C_CALL: {
|
|
Expression** it = extract(value);
|
|
Expression* func = pop();
|
|
CallExpression* ce = new CallExpression(func, value, it, code + pos);
|
|
push(ce);
|
|
if ((code[pos+1] & 0xFF00) == C_BYREF){
|
|
int nbyref_codes = 1 + (code[pos+1] & 0xFF);
|
|
uint64_t byref_mask = 0;
|
|
for(int i=0; i<nbyref_codes; i++){
|
|
byref_mask |= (uint64_t)code[pos+1+1+i] << i*4;
|
|
}
|
|
pos += 1+1+nbyref_codes;
|
|
|
|
TYPE* signature = NULL;
|
|
int npmax;
|
|
|
|
if (ce->desc){
|
|
signature = ce->desc->method.signature;
|
|
npmax = ce->desc->method.npmax;
|
|
} else if (ce->kind == FUNCTION_PRIVATE){
|
|
FunctionExpression* fe = dynamic_cast<FunctionExpression*>(func);
|
|
int index = fe->function_index;
|
|
FUNCTION* fp = &CP->load->func[index];
|
|
if (sizeof(TYPE) != sizeof(CLASS_PARAM)){
|
|
//Something has changed...
|
|
abort();
|
|
}
|
|
signature = (TYPE*)fp->param;
|
|
npmax = fp->n_param;
|
|
}
|
|
|
|
for(int i=value; i --> 0;) if (1ULL<<i & byref_mask){
|
|
if (signature && i >= npmax)
|
|
THROW(E_BYREF);
|
|
auto ex = new OnStackExpression();
|
|
ex->type = signature ? signature[i] : -1;
|
|
|
|
uint stack_start_save = stack_start;
|
|
stack_start = stack_size();
|
|
push(ex);
|
|
JIT_read_statement();
|
|
stack_start = stack_start_save;
|
|
|
|
ce->byref_expressions.push_back(pop_statement());
|
|
}
|
|
CONT
|
|
} else {
|
|
NEXT
|
|
}
|
|
}
|
|
case C_BYREF: {
|
|
int nbyref_codes = 1 + (code[pos] & 0xFF);
|
|
for(int i=0; i<nbyref_codes; i++){
|
|
func_byref_mask |= (uint64_t)code[pos+1+i] << i*4;
|
|
}
|
|
NEXT
|
|
}
|
|
case C_PUSH_INTEGER:
|
|
push(new PushIntegerExpression(32, (short)code[pos+1]));
|
|
NEXT
|
|
case C_PUSH_LONG:
|
|
push(new PushIntegerExpression(32, code[pos+1] | (code[pos+2] << 16)));
|
|
NEXT
|
|
case C_PUSH_ME: {
|
|
if (value & 2){ //Super
|
|
if (OP)
|
|
push(new PushSuperExpression());
|
|
else
|
|
push(new PushClassExpression(CP->parent));
|
|
} else {
|
|
if (OP)
|
|
push(new PushMeExpression());
|
|
else
|
|
push(new PushClassExpression(CP));
|
|
}
|
|
NEXT
|
|
}
|
|
case C_PUSH_MISC:
|
|
switch(value){
|
|
case CPM_NULL: push(new PushNullExpression()); NEXT
|
|
case CPM_VOID: push(new PushVoidExpression()); NEXT
|
|
case CPM_FALSE: push(new PushIntegerExpression(1, false)); goto push_false_true;
|
|
case CPM_TRUE: push(new PushIntegerExpression(1, true)); goto push_false_true;
|
|
case CPM_LAST: push(new PushLastExpression()); NEXT
|
|
case CPM_STRING: push(new PushCStringExpression(NULL, 0, 0)); NEXT
|
|
case CPM_PINF: push(new PushFloatExpression(64, INFINITY)); NEXT
|
|
case CPM_MINF: push(new PushFloatExpression(64, -INFINITY)); NEXT
|
|
}
|
|
assert(false && "Illegal Push Misc");
|
|
push_false_true: {
|
|
if ((code[pos+1] & 0xFF00) == C_JUMP){
|
|
//Bytecode representation of And if, Or if is implemented in this strange way
|
|
int addr = pos + 3 + code[pos+2];
|
|
//There is a JUMP IF FALSE at addr
|
|
int false_addr = addr + code[addr+1] + 2;
|
|
push_statement(new JumpIfExpression(pop(), false_addr, addr + 2, false));
|
|
pos += 3;
|
|
CONT
|
|
}
|
|
NEXT
|
|
}
|
|
case C_ON: {
|
|
int num_cases = value;
|
|
std::vector<int> destinations;
|
|
destinations.resize(num_cases);
|
|
for(int i=0; i<num_cases; i++){
|
|
destinations[i] = pos + 1 + i + (short)code[pos+i+1];
|
|
}
|
|
pos += 1 + num_cases;
|
|
if ((code[pos] & 0xFF00) == C_JUMP){
|
|
//Goto
|
|
push_statement(new OnGotoExpression(pop(), std::move(destinations), pos + 2));
|
|
} else {
|
|
//GoSub
|
|
ngosubs++;
|
|
push_statement(new GosubExpression(code[pos] & 0xFF, pop(), std::move(destinations), pos + 2));
|
|
}
|
|
pos += 2;
|
|
CONT
|
|
}
|
|
case C_JUMP:
|
|
/*if (EC && code + pos + (short)code[pos+1] + 2 >= EC){
|
|
push_statement(new EndTryExpression());
|
|
push_statement(new JumpExpression(pos + (short)code[pos+1] + 2 + 1));
|
|
} else {*/
|
|
push_statement(new JumpExpression(pos + (short)code[pos+1] + 2));
|
|
//}
|
|
NEXT
|
|
case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE:
|
|
push_statement(new JumpIfExpression(pop(), pos + (short)code[pos+1] + 2, pos + 2, digit == C_JUMP_IF_TRUE));
|
|
NEXT
|
|
case C_JUMP_FIRST: {
|
|
Expression* step = pop();
|
|
Expression* to = pop();
|
|
push_statement(new JumpFirstExpression(value, to, step, code[pos+3] & 0xFF, pos + 4, pos + code[pos+2] + 3));
|
|
NEXT
|
|
}
|
|
case C_JUMP_NEXT:
|
|
push_statement(new JumpNextExpression(code[pos-1] & 0xFF, code[pos+2] & 0xFF, pos + 3, pos + code[pos+1] + 2));
|
|
pos += 3;
|
|
CONT
|
|
case C_GOSUB: {
|
|
ngosubs++;
|
|
push_statement(new GosubExpression(value, pos + (short)code[pos+1] + 2));
|
|
NEXT
|
|
}
|
|
case C_FIRST: {
|
|
Expression* obj = pop();
|
|
push_statement(new JumpEnumFirstExpression(value, obj));
|
|
NEXT
|
|
}
|
|
case C_NEXT:
|
|
if (value) //Drop
|
|
push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, true, NULL));
|
|
else {
|
|
OnStackExpression* ose = new OnStackExpression();
|
|
push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, false, ose));
|
|
push(ose);
|
|
addr = pos+2;
|
|
}
|
|
NEXT
|
|
case C_DROP:
|
|
push_statement(new DropExpression(pop()));
|
|
NEXT
|
|
case C_DUP:
|
|
in_dup = true; //Becomes false again after Pop Ctrl
|
|
push(new DupExpression(top()));
|
|
NEXT
|
|
case C_NEW: {
|
|
int nargs = value & 0x3F;
|
|
Expression** it = extract(nargs);
|
|
push(new NewExpression(it, nargs, value & CODE_NEW_EVENT));
|
|
NEXT
|
|
}
|
|
case C_BREAK:
|
|
//PC = code+pos;
|
|
push_statement(new NopExpression(true/*JIF.F_DEBUG_get_current_position()*/));
|
|
//PC = code;
|
|
//push_statement(new NopExpression());
|
|
NEXT
|
|
case C_RETURN:
|
|
push_statement(new ReturnExpression(value == 1 ? pop() : NULL, value));
|
|
NEXT
|
|
case C_QUIT:
|
|
switch(value){
|
|
case 0: push_statement(new QuitExpression()); NEXT
|
|
case 1: push_statement(new NopExpression(false)); NEXT //FIXME breakpoint
|
|
case 2: default: push_statement(new StopEventExpression()); NEXT
|
|
}
|
|
case C_PUSH_CHAR:
|
|
push(new PushCStringExpression((char*)&STRING_char_string[value * 2], 0, 1));
|
|
NEXT
|
|
case C_TRY:
|
|
push_statement(new TryExpression(pos + 2, pos + code[pos+1] + 2));
|
|
NEXT
|
|
case C_END_TRY:
|
|
push_statement(new EndTryExpression());
|
|
NEXT
|
|
case C_CATCH:
|
|
if (code + pos < EC) // No finally
|
|
push_statement(new ReturnExpression(NULL, 2));
|
|
else // We have a finally somewhere above
|
|
push_statement(new CatchExpression());
|
|
NEXT
|
|
|
|
case C_EQ: push(new EqExpression(extract(2))); NEXT
|
|
case C_NE: push(new NotExpression(new EqExpression(extract(2)))); NEXT
|
|
case C_GT: push(new LessExpression(extract_reverse(2))); NEXT
|
|
case C_LE: push(new NotExpression(new LessExpression(extract_reverse(2)))); NEXT
|
|
case C_LT: push(new LessExpression(extract(2))); NEXT
|
|
case C_GE: push(new NotExpression(new LessExpression(extract(2)))); NEXT
|
|
case C_NEAR: push(new NearExpression(extract(2))); NEXT
|
|
case C_CASE: assert(false && "C_CASE is not used anymore?");
|
|
#define op(_c, _n) case _c: push(new _n##Expression(extract(2))); NEXT
|
|
op(C_ADD, Add)
|
|
op(C_SUB, Sub)
|
|
op(C_MUL, Mul)
|
|
op(C_DIV, Div)
|
|
|
|
op(C_QUO, Quo)
|
|
op(C_REM, Rem)
|
|
op(C_POW, Pow)
|
|
op(C_AND, And)
|
|
op(C_OR, Or)
|
|
op(C_XOR, Xor)
|
|
|
|
op(C_IS, Is)
|
|
#undef op
|
|
case C_NEG: push(new NegExpression(pop())); NEXT
|
|
case C_NOT: push(new NotExpression(pop())); NEXT
|
|
case C_CAT: push(new CatExpression(extract(value), value)); NEXT
|
|
case C_FILE: push(new FileExpression(extract(value), value)); NEXT
|
|
case C_LIKE: push(new LikeExpression(extract(2), value)); NEXT
|
|
|
|
|
|
default: {
|
|
digit >>= 8U;
|
|
// 0x40 <= digit <= 0x9F
|
|
int extra = value & 0x3F;
|
|
int nargs = get_subr_nargs(digit, extra);
|
|
switch(digit){
|
|
case 0x67: //CBool ...
|
|
JIT_conv(top(), extra);
|
|
goto _next;
|
|
case 0x6B: //Str
|
|
if (TYPE_is_string(top()->type))
|
|
goto _next;
|
|
break;
|
|
}
|
|
push(new SubrExpression(digit, extract(nargs), nargs, extra));
|
|
_next:
|
|
NEXT
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__NEXT:
|
|
if (stack_empty()) //Next code row
|
|
return;
|
|
}
|
|
}
|
|
|
|
void JIT_read(){
|
|
code = FP->code;
|
|
size = ((int*)code)[-1] / sizeof(short);
|
|
if (FP->code[size-1] == 0)
|
|
--size;
|
|
|
|
pos = 0;
|
|
|
|
TRY {
|
|
if (EC != NULL){
|
|
addr = -1;
|
|
push_statement(new LargeTryExpression());
|
|
}
|
|
|
|
func_byref_mask = 0;
|
|
|
|
stack_start = 0;
|
|
while(pos < size){
|
|
addr = pos;
|
|
PC = code + pos;
|
|
JIT_read_statement();
|
|
}
|
|
|
|
std::sort(taken_addresses.begin(), taken_addresses.end());
|
|
for(size_t i=0, j=0, e1=taken_addresses.size(), e2=all_statements.size(); i!=e1 && j!=e2;){
|
|
int a = taken_addresses[i];
|
|
int b = all_statements[j]->addr;
|
|
if (a == b){
|
|
all_statements[j]->address_taken = true;
|
|
i++, j++;
|
|
} else if (a < b){
|
|
i++;
|
|
} else if (a > b){
|
|
j++;
|
|
}
|
|
}
|
|
taken_addresses.clear();
|
|
}
|
|
CATCH {
|
|
//puts("Fel:");
|
|
//ERROR_print_at(stderr, 1, 1);
|
|
//abort();
|
|
for(size_t i=0, e=all_statements.size(); i!=e; i++)
|
|
delete all_statements[i];
|
|
all_statements.clear();
|
|
free_all_expressions();
|
|
taken_addresses.clear();
|
|
JIF.F_ERROR_propagate();
|
|
}
|
|
END_TRY
|
|
}
|