#include "objectscript.h"
#include "os-binder.h"
#include <time.h>
using namespace ObjectScript;
#define HASH_GROW_SHIFT 0
// =====================================================================
// =====================================================================
// =====================================================================
#if defined __GNUC__ || defined IW_SDK
int OS_VSNPRINTF(OS_CHAR * str, size_t size, const OS_CHAR *format, va_list va)
{
return vsnprintf(str, size, format, va);
}
#else
int OS_VSNPRINTF(OS_CHAR * str, size_t size, const OS_CHAR *format, va_list va)
{
return vsnprintf_s(str, size, size/sizeof(OS_CHAR), format, va);
}
#endif
int OS_SNPRINTF(OS_CHAR * str, size_t size, const OS_CHAR *format, ...)
{
va_list va;
va_start(va, format);
int ret = OS_VSNPRINTF(str, size, format, va);
va_end(va);
return ret;
}
static bool OS_ISNAN(float a)
{
volatile float b = a;
return b != b;
}
static bool OS_ISNAN(double a)
{
volatile double b = a;
return b != b;
}
#include <float.h>
#include <limits.h>
template <class T> T OS_getMaxValue();
template <> double OS_getMaxValue<double>(){ return DBL_MAX; }
template <> float OS_getMaxValue<float>(){ return FLT_MAX; }
template <> int OS_getMaxValue<int>(){ return INT_MAX; }
#define OS_MAX_NUMBER OS_getMaxValue<OS_NUMBER>()
#define CURRENT_BYTE_ORDER (*(OS_INT32*)"\x01\x02\x03\x04")
#define LITTLE_ENDIAN_BYTE_ORDER 0x04030201
#define BIG_ENDIAN_BYTE_ORDER 0x01020304
#define PDP_ENDIAN_BYTE_ORDER 0x02010403
#define IS_LITTLE_ENDIAN (CURRENT_BYTE_ORDER == LITTLE_ENDIAN_BYTE_ORDER)
#define IS_BIG_ENDIAN (CURRENT_BYTE_ORDER == BIG_ENDIAN_BYTE_ORDER)
#define IS_PDP_ENDIAN (CURRENT_BYTE_ORDER == PDP_ENDIAN_BYTE_ORDER)
static inline OS_BYTE toLittleEndianByteOrder(OS_BYTE val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*1);
return val;
}
static inline OS_INT8 toLittleEndianByteOrder(OS_INT8 val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*1);
return val;
}
static inline OS_U16 toLittleEndianByteOrder(OS_U16 val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*2);
if(IS_LITTLE_ENDIAN){
return val;
}
OS_U16 r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[0];
return r;
}
static inline OS_INT16 toLittleEndianByteOrder(OS_INT16 val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*2);
if(IS_LITTLE_ENDIAN){
return val;
}
OS_INT16 r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[0];
return r;
}
static inline OS_INT32 toLittleEndianByteOrder(OS_INT32 val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*4);
if(IS_LITTLE_ENDIAN){
return val;
}
OS_INT32 r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[3];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[2];
((OS_BYTE*)&r)[2] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[3] = ((OS_BYTE*)&val)[0];
return r;
}
static inline OS_INT64 toLittleEndianByteOrder(OS_INT64 val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*8);
if(IS_LITTLE_ENDIAN){
return val;
}
OS_INT64 r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[7];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[6];
((OS_BYTE*)&r)[2] = ((OS_BYTE*)&val)[5];
((OS_BYTE*)&r)[3] = ((OS_BYTE*)&val)[4];
((OS_BYTE*)&r)[4] = ((OS_BYTE*)&val)[3];
((OS_BYTE*)&r)[5] = ((OS_BYTE*)&val)[2];
((OS_BYTE*)&r)[6] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[7] = ((OS_BYTE*)&val)[0];
return r;
}
static inline float toLittleEndianByteOrder(float val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*4);
if(IS_LITTLE_ENDIAN){
return val;
}
float r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[3];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[2];
((OS_BYTE*)&r)[2] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[3] = ((OS_BYTE*)&val)[0];
return r;
}
static inline double toLittleEndianByteOrder(double val)
{
OS_ASSERT(sizeof(val) == sizeof(OS_BYTE)*8);
if(IS_LITTLE_ENDIAN){
return val;
}
double r;
((OS_BYTE*)&r)[0] = ((OS_BYTE*)&val)[7];
((OS_BYTE*)&r)[1] = ((OS_BYTE*)&val)[6];
((OS_BYTE*)&r)[2] = ((OS_BYTE*)&val)[5];
((OS_BYTE*)&r)[3] = ((OS_BYTE*)&val)[4];
((OS_BYTE*)&r)[4] = ((OS_BYTE*)&val)[3];
((OS_BYTE*)&r)[5] = ((OS_BYTE*)&val)[2];
((OS_BYTE*)&r)[6] = ((OS_BYTE*)&val)[1];
((OS_BYTE*)&r)[7] = ((OS_BYTE*)&val)[0];
return r;
}
#define fromLittleEndianByteOrder toLittleEndianByteOrder
static const OS_INT32 nan_data = 0x7fc00000;
static const float nan_float = fromLittleEndianByteOrder(*(float*)&nan_data);
static inline void parseSpaces(const OS_CHAR *& str)
{
while(*str && OS_IS_SPACE(*str))
str++;
}
template <class T>
static bool parseSimpleHex(const OS_CHAR *& p_str, T& p_val)
{
T val = 0, prev_val = 0;
const OS_CHAR * str = p_str;
const OS_CHAR * start = str;
for(;; str++){
if(*str >= OS_TEXT('0') && *str <= OS_TEXT('9')){
val = (val << 4) + (T)(*str - OS_TEXT('0'));
}else if(*str >= OS_TEXT('a') && *str <= OS_TEXT('f')){
val = (val << 4) + 10 + (T)(*str - OS_TEXT('a'));
}else if(*str >= OS_TEXT('A') && *str <= OS_TEXT('F')){
val = (val << 4) + 10 + (T)(*str - OS_TEXT('A'));
}else{
break;
}
if(prev_val > val){
p_str = start;
p_val = 0;
return false;
}
prev_val = val;
}
p_val = val;
p_str = str;
return str > start;
}
template <class T>
static bool parseSimpleBin(const OS_CHAR *& p_str, T& p_val)
{
T val = 0, prev_val = 0;
const OS_CHAR * str = p_str;
const OS_CHAR * start = str;
for(; *str >= OS_TEXT('0') && *str <= OS_TEXT('1'); str++){
val = (val << 1) + (T)(*str - OS_TEXT('0'));
if(prev_val > val){
p_str = start;
p_val = 0;
return false;
}
prev_val = val;
}
p_val = val;
p_str = str;
return str > start;
}
template <class T>
static bool parseSimpleOctal(const OS_CHAR *& p_str, T& p_val)
{
T val = 0, prev_val = 0;
const OS_CHAR * str = p_str;
const OS_CHAR * start = str;
for(; *str >= OS_TEXT('0') && *str <= OS_TEXT('7'); str++)
{
val = (val << 3) + (T)(*str - OS_TEXT('0'));
if(prev_val > val){
p_str = start;
p_val = 0;
return false;
}
prev_val = val;
}
p_val = val;
p_str = str;
return str > start;
}
template <class T>
static bool parseSimpleDec(const OS_CHAR *& p_str, T& p_val)
{
T val = 0, prev_val = 0;
const OS_CHAR * str = p_str;
const OS_CHAR * start = str;
for(; *str >= OS_TEXT('0') && *str <= OS_TEXT('9'); str++){
val = val * 10 + (T)(*str - OS_TEXT('0'));
if(prev_val > val){
p_str = start;
p_val = 0;
return false;
}
prev_val = val;
}
p_val = val;
p_str = str;
return str > start;
}
template <class T>
static bool parseSimpleFloat(const OS_CHAR *& p_str, T& p_val)
{
T val = 0;
const OS_CHAR * str = p_str;
const OS_CHAR * start = str;
for(; *str >= OS_TEXT('0') && *str <= OS_TEXT('9'); str++){
val = val * 10 + (*str - OS_TEXT('0'));
}
p_val = val;
p_str = str;
return str > start;
}
bool OS::Utils::parseFloat(const OS_CHAR *& str, OS_FLOAT& result)
{
const OS_CHAR * start_str = str;
int sign = 1;
if(*str == OS_TEXT('-')){
str++;
start_str++;
sign = -1;
}else if(*str == OS_TEXT('+')){
str++;
start_str++;
}
if(str[0] == OS_TEXT('0') && str[1] != OS_TEXT('.')){
bool is_valid, is_octal = false;
OS_INT int_val;
if(str[1] == OS_TEXT('x') || str[1] == OS_TEXT('X')){ // parse hex
str += 2;
is_valid = parseSimpleHex(str, int_val);
}else if(str[1] == OS_TEXT('b') || str[1] == OS_TEXT('B')){ // parse hex
str += 2;
is_valid = parseSimpleBin(str, int_val);
}else{ // parse octal
is_octal = true;
is_valid = parseSimpleOctal(str, int_val);
}
if(!is_valid || (start_str+1 == str && !is_octal)){
result = 0;
return false;
}
if((OS_INT)(OS_FLOAT)int_val != int_val){
result = 0;
return false;
}
result = (OS_FLOAT)int_val;
return true;
}
OS_FLOAT float_val;
if(!parseSimpleFloat(str, float_val)){
result = 0;
return false;
}
if(*str == OS_TEXT('.')){ // parse float
// parse 1.#INF ...
if(sign == 1 && start_str+1 == str && *start_str == OS_TEXT('1') && str[1] == OS_TEXT('#')){
const OS_CHAR * spec[] = {OS_TEXT("INF"), OS_TEXT("IND"), OS_TEXT("QNAN"), NULL};
int i = 0;
for(; spec[i]; i++){
if(OS_STRCMP(str, spec[i]) != 0)
continue;
size_t specLen = OS_STRLEN(spec[i]);
str += specLen;
if(!*str || OS_IS_SPACE(*str) || OS_STRCHR(OS_TEXT("!@#$%^&*()-+={}[]\\|;:'\",<.>/?`~"), *str)){
OS_INT32 spec_val;
switch(i){
case 0:
spec_val = 0x7f800000;
break;
case 1:
spec_val = 0xffc00000;
break;
default:
OS_ASSERT(false);
// no break
case 2:
spec_val = 0x7fc00000;
break;
}
result = (OS_FLOAT)fromLittleEndianByteOrder(*(float*)&spec_val);
return true;
}
}
result = 0;
return false;
}
OS_FLOAT m = 0.1;
for(str++; *str >= OS_TEXT('0') && *str <= OS_TEXT('9'); str++, m *= 0.1){
float_val += (OS_FLOAT)(*str - OS_TEXT('0')) * m;
}
if(start_str == str){
result = 0;
return false;
}
if(*str == OS_TEXT('e') || *str == OS_TEXT('E')){
str++;
bool div = false; // + for default
if(*str == OS_TEXT('-')){
div = true;
str++;
}else if(*str == OS_TEXT('+')){
// div = false;
str++;
}
int pow;
if(!parseSimpleDec(str, pow)){
result = 0;
return false;
}
m = 1.0f;
for(int i = 0; i < pow; i++){
m *= 10.0f;
}
if(div){
float_val /= m;
}else{
float_val *= m;
}
}
result = sign > 0 ? float_val : -float_val;
return true;
}
if(start_str == str){
result = 0;
return false;
}
result = sign > 0 ? float_val : -float_val;
return true;
}
OS_CHAR * OS::Utils::numToStr(OS_CHAR * dst, OS_INT32 a)
{
OS_SNPRINTF(dst, sizeof(OS_CHAR)*63, OS_TEXT("%i"), a);
return dst;
}
OS_CHAR * OS::Utils::numToStr(OS_CHAR * dst, OS_INT64 a)
{
OS_SNPRINTF(dst, sizeof(OS_CHAR)*63, OS_TEXT("%li"), (long int)a);
return dst;
}
OS_CHAR * OS::Utils::numToStr(OS_CHAR * dst, float a, int precision)
{
return numToStr(dst, (double)a, precision);
}
OS_CHAR * OS::Utils::numToStr(OS_CHAR * dst, double a, int precision)
{
OS_CHAR buf[128];
if(precision <= 0) {
if(precision < 0) {
OS_FLOAT p = 10.0f;
for(int i = -precision-1; i > 0; i--){
p *= 10.0f;
}
a = ::floor(a / p + 0.5f) * p;
}
OS_SNPRINTF(dst, sizeof(buf)-sizeof(OS_CHAR), OS_TEXT("%.f"), a);
return dst;
}
if(precision == OS_AUTO_PRECISION){
/* %G already handles removing trailing zeros from the fractional part, yay */
OS_SNPRINTF(dst, sizeof(buf)-sizeof(OS_CHAR), OS_TEXT("%.*G"), 17, a);
return dst;
}
OS_SNPRINTF(buf, sizeof(buf)-sizeof(OS_CHAR), OS_TEXT("%%.%df"), precision);
int n = OS_SNPRINTF(dst, sizeof(buf)-sizeof(OS_CHAR), buf, a);
OS_ASSERT(n >= 1 && !OS_STRSTR(dst, OS_TEXT(".")) || dst[n-1] != '0');
/* if(n > 0 && dst[n-1] == '0'){
do{ dst[--n] = (OS_CHAR)0; }while(n > 0 && dst[n-1] == '0');
if(n > 0 && dst[n-1] == '.') dst[--n] = (OS_CHAR)0;
} */
return dst;
}
OS_INT OS::Utils::strToInt(const OS_CHAR * str)
{
return (OS_INT)strToFloat(str);
}
OS_FLOAT OS::Utils::strToFloat(const OS_CHAR* str)
{
OS_FLOAT fval;
if(parseFloat(str, fval) && (!*str || (*str==OS_TEXT('f') && !str[1]))){
return fval;
}
return 0;
}
#define OS_KEY_HASH_START_VALUE 5381
#define OS_ADD_KEY_HASH_VALUE hash = ((hash << 5) + hash) + *buf++
int OS::Utils::keyToHash(const void * buf, int size)
{
return OS::Utils::addKeyToHash(OS_KEY_HASH_START_VALUE, buf, size);
}
int OS::Utils::keyToHash(const void * buf1, int size1, const void * buf2, int size2)
{
int hash = OS::Utils::addKeyToHash(OS_KEY_HASH_START_VALUE, buf1, size1);
return OS::Utils::addKeyToHash(hash, buf2, size2);
}
int OS::Utils::addKeyToHash(int hash, const void * p_buf, int size)
{
const OS_BYTE * buf = (const OS_BYTE*)p_buf;
for(; size >= 8; size -= 8) {
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
OS_ADD_KEY_HASH_VALUE;
}
switch(size) {
case 7: OS_ADD_KEY_HASH_VALUE;
case 6: OS_ADD_KEY_HASH_VALUE;
case 5: OS_ADD_KEY_HASH_VALUE;
case 4: OS_ADD_KEY_HASH_VALUE;
case 3: OS_ADD_KEY_HASH_VALUE;
case 2: OS_ADD_KEY_HASH_VALUE;
case 1: OS_ADD_KEY_HASH_VALUE;
}
return hash;
}
int OS::Utils::cmp(const void * buf1, int len1, const void * buf2, int len2)
{
int len = len1 < len2 ? len1 : len2;
int cmp = OS_MEMCMP(buf1, buf2, len);
return cmp ? cmp : len1 - len2;
}
/*
int OS::Utils::cmp(const void * buf1, int len1, const void * buf2, int len2, int maxLen)
{
return cmp(buf1, len1 < maxLen ? len1 : maxLen, buf2, len2 < maxLen ? len2 : maxLen);
}
*/
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::String::String(OS * os)
{
string = os->core->newStringValue((void*)NULL, 0);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(GCStringValue * s)
{
string = s;
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(const String& s)
{
string = s.string;
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const String& a, const String& b)
{
string = os->core->newStringValue(a, b);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const OS_CHAR * str)
{
string = os->core->newStringValue(str);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const OS_CHAR * str, int len)
{
string = os->core->newStringValue(str, len);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const OS_CHAR * str, int len, const OS_CHAR * str2, int len2)
{
string = os->core->newStringValue(str, len, str2, len2);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const OS_CHAR * str, int len, bool trim_left, bool trim_right)
{
string = os->core->newStringValue(str, len, trim_left, trim_right);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const void * buf, int size)
{
string = os->core->newStringValue(buf, size);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const void * buf1, int size1, const void * buf2, int size2)
{
string = os->core->newStringValue(buf1, size1, buf2, size2);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, const void * buf1, int size1, const void * buf2, int size2, const void * buf3, int size3)
{
string = os->core->newStringValue(buf1, size1, buf2, size2, buf3, size3);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, OS_INT value)
{
string = os->core->newStringValue(value);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::String(OS * os, OS_FLOAT value, int precision)
{
string = os->core->newStringValue(value, precision);
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
OS::Core::String::~String()
{
if(string){ // can be cleared by OS::~String
OS_ASSERT(string->external_ref_count > 0);
string->external_ref_count--;
if(string->gc_color == GC_WHITE){
string->gc_color = GC_BLACK;
}
}
}
struct OS_VaListDtor
{
va_list * va;
OS_VaListDtor(va_list * p_va){ va = p_va; }
~OS_VaListDtor(){ va_end(*va); }
};
OS::Core::String OS::Core::String::format(OS * allocator, int temp_buf_len, const OS_CHAR * fmt, ...)
{
va_list va;
va_start(va, fmt);
OS_VaListDtor va_dtor(&va);
return String(allocator->core->newStringValueVa(temp_buf_len, fmt, va));
}
OS::Core::String OS::Core::String::formatVa(OS * allocator, int temp_buf_len, const OS_CHAR * fmt, va_list va)
{
return String(allocator->core->newStringValueVa(temp_buf_len, fmt, va));
}
OS::Core::String OS::Core::String::format(OS * allocator, const OS_CHAR * fmt, ...)
{
va_list va;
va_start(va, fmt);
OS_VaListDtor va_dtor(&va);
return String(allocator->core->newStringValueVa(OS_DEF_FMT_BUF_LEN, fmt, va));
}
OS::Core::String OS::Core::String::formatVa(OS * allocator, const OS_CHAR * fmt, va_list va)
{
return String(allocator->core->newStringValueVa(OS_DEF_FMT_BUF_LEN, fmt, va));
}
OS::Core::String& OS::Core::String::operator=(const String& b)
{
if(string != b.string){
OS_ASSERT(string->external_ref_count > 0);
string->external_ref_count--;
if(string->gc_color == GC_WHITE){
string->gc_color = GC_BLACK;
}
string = b.string;
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
return *this;
}
bool OS::Core::String::operator==(const String& b) const
{
return string == b.string;
}
bool OS::Core::String::operator==(const OS_CHAR * b) const
{
return cmp(b) == 0;
}
bool OS::Core::String::operator==(GCStringValue * b) const
{
return string == b;
}
bool OS::Core::String::operator!=(const String& b) const
{
return string != b.string;
}
bool OS::Core::String::operator!=(const OS_CHAR * b) const
{
return cmp(b) != 0;
}
bool OS::Core::String::operator!=(GCStringValue * b) const
{
return string != b;
}
bool OS::Core::String::operator<=(const String& b) const
{
return cmp(b) <= 0;
}
bool OS::Core::String::operator<=(const OS_CHAR * b) const
{
return cmp(b) <= 0;
}
bool OS::Core::String::operator<(const String& b) const
{
return cmp(b) < 0;
}
bool OS::Core::String::operator<(const OS_CHAR * b) const
{
return cmp(b) < 0;
}
bool OS::Core::String::operator>=(const String& b) const
{
return cmp(b) >= 0;
}
bool OS::Core::String::operator>=(const OS_CHAR * b) const
{
return cmp(b) >= 0;
}
bool OS::Core::String::operator>(const String& b) const
{
return cmp(b) > 0;
}
bool OS::Core::String::operator>(const OS_CHAR * b) const
{
return cmp(b) > 0;
}
int OS::Core::String::cmp(const String& b) const
{
if(string == b.string){
return 0;
}
return Utils::cmp(string->toChar(), string->data_size, b.string->toChar(), b.string->data_size);
}
int OS::Core::String::cmp(const OS_CHAR * b) const
{
return Utils::cmp(string->toChar(), string->data_size, b, OS_STRLEN(b));
}
int OS::Core::String::getHash() const
{
return string->hash;
}
OS_NUMBER OS::Core::String::toNumber() const
{
OS_NUMBER val;
if(string->isNumber(&val)){
return val;
}
return 0;
}
// =====================================================================
OS::Core::StringBuffer::StringBuffer(OS * p_allocator)
{
allocator = p_allocator;
}
OS::Core::StringBuffer::~StringBuffer()
{
allocator->vectorClear(*this);
}
OS::Core::StringBuffer& OS::Core::StringBuffer::append(OS_CHAR c)
{
allocator->vectorAddItem(*this, c OS_DBG_FILEPOS);
return *this;
}
OS::Core::StringBuffer& OS::Core::StringBuffer::append(const OS_CHAR * str)
{
return append(str, OS_STRLEN(str));
}
OS::Core::StringBuffer& OS::Core::StringBuffer::append(const OS_CHAR * str, int len)
{
allocator->vectorReserveCapacity(*this, count + len OS_DBG_FILEPOS);
OS_MEMCPY(buf + count, str, len * sizeof(OS_CHAR));
count += len;
return *this;
}
OS::Core::StringBuffer& OS::Core::StringBuffer::append(const String& str)
{
return append(str.toChar(), str.getLen());
}
OS::Core::StringBuffer& OS::Core::StringBuffer::append(const StringBuffer& buf)
{
return append(buf.buf, buf.count);
}
OS::Core::StringBuffer& OS::Core::StringBuffer::operator+=(const String& str)
{
return append(str);
}
OS::Core::StringBuffer& OS::Core::StringBuffer::operator+=(const OS_CHAR * str)
{
return append(str);
}
OS::Core::StringBuffer::operator OS::Core::String() const
{
return toString();
}
OS::Core::String OS::Core::StringBuffer::toString() const
{
return String(allocator, buf, count);
}
OS::Core::GCStringValue * OS::Core::StringBuffer::toGCStringValue() const
{
return allocator->core->newStringValue(buf, count);
}
// =====================================================================
OS::String::String(OS * allocator): super(allocator)
{
this->allocator = allocator->retain();
}
OS::String::String(const String& str): super(str)
{
allocator = str.allocator->retain();
}
OS::String::String(OS * allocator, const Core::String& str): super(str)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const OS_CHAR * str): super(allocator, str)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const OS_CHAR * str1, int len1, const OS_CHAR * str2, int len2): super(allocator, str1, len1, str2, len2)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const OS_CHAR * str, int len): super(allocator, str, len)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const OS_CHAR * str, int len, bool trim_left, bool trim_right): super(allocator, str, len, trim_left, trim_right)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const void * buf, int size): super(allocator, buf, size)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, const void * buf1, int size1, const void * buf2, int size2): super(allocator, buf1, size1, buf2, size2)
{
this->allocator = allocator->retain();
}
/*
OS::String::String(OS * allocator, const void * buf1, int size1, const void * buf2, int size2, const void * buf3, int size3): super(allocator, buf1, size1, buf2, size2, buf3, size3)
{
this->allocator = allocator->retain();
}
*/
OS::String::String(OS * allocator, OS_INT value): super(allocator, value)
{
this->allocator = allocator->retain();
}
OS::String::String(OS * allocator, OS_FLOAT value, int precision): super(allocator, value, precision)
{
this->allocator = allocator->retain();
}
OS::String::~String()
{
OS_ASSERT(string->external_ref_count > 0);
string->external_ref_count--;
if(string->gc_color == Core::GC_WHITE){
string->gc_color = Core::GC_BLACK;
}
string = NULL;
allocator->release();
}
OS::String& OS::String::operator=(const Core::String& str)
{
if(string != str.string){
OS_ASSERT(string->external_ref_count > 0);
string->external_ref_count--;
if(string->gc_color == Core::GC_WHITE){
string->gc_color = Core::GC_BLACK;
}
string = str.string;
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
return *this;
}
OS::String& OS::String::operator=(const String& str)
{
OS_ASSERT(allocator == str.allocator);
if(string != str.string){
OS_ASSERT(string->external_ref_count > 0);
string->external_ref_count--;
if(string->gc_color == Core::GC_WHITE){
string->gc_color = Core::GC_BLACK;
}
string = str.string;
string->external_ref_count++;
#ifdef OS_DEBUG
this->str = string->toChar();
#endif
}
return *this;
}
OS::String& OS::String::operator+=(const String& str)
{
return *this = allocator->core->newStringValue(*this, str);
}
OS::String& OS::String::operator+=(const OS_CHAR * str)
{
return *this = allocator->core->newStringValue(toChar(), getDataSize(), str, OS_STRLEN(str)*sizeof(OS_CHAR));
}
OS::String OS::String::operator+(const String& str) const
{
return String(allocator, allocator->core->newStringValue(*this, str));
}
OS::String OS::String::operator+(const OS_CHAR * str) const
{
return String(allocator, allocator->core->newStringValue(toChar(), getDataSize(), str, OS_STRLEN(str)*sizeof(OS_CHAR)));
}
OS::String OS::String::trim(bool trim_left, bool trim_right) const
{
return String(allocator, allocator->core->newStringValue(*this, trim_left, trim_right));
}
// =====================================================================
// =====================================================================
// =====================================================================
const OS_CHAR * OS::Core::Tokenizer::getTokenTypeName(TokenType token_type)
{
switch(token_type){
case NOTHING: return OS_TEXT("NOTHING");
case BEGIN_CODE_BLOCK: return OS_TEXT("BEGIN_CODE_BLOCK");
case END_CODE_BLOCK: return OS_TEXT("END_CODE_BLOCK");
case BEGIN_BRACKET_BLOCK: return OS_TEXT("BEGIN_BRACKET_BLOCK");
case END_BRACKET_BLOCK: return OS_TEXT("END_BRACKET_BLOCK");
case BEGIN_ARRAY_BLOCK: return OS_TEXT("BEGIN_ARRAY_BLOCK");
case END_ARRAY_BLOCK: return OS_TEXT("END_ARRAY_BLOCK");
case CODE_SEPARATOR: return OS_TEXT("CODE_SEPARATOR");
case PARAM_SEPARATOR: return OS_TEXT("PARAM_SEPARATOR");
case COMMENT_LINE: return OS_TEXT("COMMENT_LINE");
case COMMENT_MULTI_LINE: return OS_TEXT("COMMENT_MULTI_LINE");
case NAME: return OS_TEXT("NAME");
// case DOT_NAME: return OS_TEXT("DOT_NAME");
// case IDENTIFER: return OS_TEXT("IDENTIFER");
// case DOT_IDENTIFER: return OS_TEXT("DOT_IDENTIFER");
case STRING: return OS_TEXT("STRING");
case NUMBER: return OS_TEXT("NUMBER");
// case NUM_VECTOR_3: return OS_TEXT("NUM_VECTOR_3");
// case NUM_VECTOR_4: return OS_TEXT("NUM_VECTOR_4");
case OPERATOR: return OS_TEXT("OPERATOR");
case BINARY_OPERATOR: return OS_TEXT("BINARY_OPERATOR");
case OPERATOR_INDIRECT: return OS_TEXT("OPERATOR_INDIRECT");
case OPERATOR_CONCAT: return OS_TEXT("OPERATOR_CONCAT");
case OPERATOR_LOGIC_AND: return OS_TEXT("OPERATOR_LOGIC_AND");
case OPERATOR_LOGIC_OR: return OS_TEXT("OPERATOR_LOGIC_OR");
case OPERATOR_LOGIC_PTR_EQ: return OS_TEXT("OPERATOR_LOGIC_PTR_EQ");
case OPERATOR_LOGIC_PTR_NE: return OS_TEXT("OPERATOR_LOGIC_PTR_NE");
case OPERATOR_LOGIC_EQ: return OS_TEXT("OPERATOR_LOGIC_EQ");
case OPERATOR_LOGIC_NE: return OS_TEXT("OPERATOR_LOGIC_NE");
case OPERATOR_LOGIC_GE: return OS_TEXT("OPERATOR_LOGIC_GE");
case OPERATOR_LOGIC_LE: return OS_TEXT("OPERATOR_LOGIC_LE");
case OPERATOR_LOGIC_GREATER: return OS_TEXT("OPERATOR_LOGIC_GREATER");
case OPERATOR_LOGIC_LESS: return OS_TEXT("OPERATOR_LOGIC_LESS");
case OPERATOR_LOGIC_NOT: return OS_TEXT("OPERATOR_LOGIC_NOT");
case OPERATOR_INC: return OS_TEXT("OPERATOR_INC");
case OPERATOR_DEC: return OS_TEXT("OPERATOR_DEC");
case OPERATOR_QUESTION: return OS_TEXT("OPERATOR_QUESTION");
case OPERATOR_COLON: return OS_TEXT("OPERATOR_COLON");
case OPERATOR_BIT_AND: return OS_TEXT("OPERATOR_BIT_AND");
case OPERATOR_BIT_OR: return OS_TEXT("OPERATOR_BIT_OR");
case OPERATOR_BIT_XOR: return OS_TEXT("OPERATOR_BIT_XOR");
case OPERATOR_BIT_NOT: return OS_TEXT("OPERATOR_BIT_NOT");
case OPERATOR_ADD: return OS_TEXT("OPERATOR_ADD");
case OPERATOR_SUB: return OS_TEXT("OPERATOR_SUB");
case OPERATOR_MUL: return OS_TEXT("OPERATOR_MUL");
case OPERATOR_DIV: return OS_TEXT("OPERATOR_DIV");
case OPERATOR_MOD: return OS_TEXT("OPERATOR_MOD");
case OPERATOR_LSHIFT: return OS_TEXT("OPERATOR_LSHIFT");
case OPERATOR_RSHIFT: return OS_TEXT("OPERATOR_RSHIFT");
case OPERATOR_POW: return OS_TEXT("OPERATOR_POW");
case OPERATOR_BIT_AND_ASSIGN: return OS_TEXT("OPERATOR_BIT_AND_ASSIGN");
case OPERATOR_BIT_OR_ASSIGN: return OS_TEXT("OPERATOR_BIT_OR_ASSIGN");
case OPERATOR_BIT_XOR_ASSIGN: return OS_TEXT("OPERATOR_BIT_XOR_ASSIGN");
case OPERATOR_BIT_NOT_ASSIGN: return OS_TEXT("OPERATOR_BIT_NOT_ASSIGN");
case OPERATOR_ADD_ASSIGN: return OS_TEXT("OPERATOR_ADD_ASSIGN");
case OPERATOR_SUB_ASSIGN: return OS_TEXT("OPERATOR_SUB_ASSIGN");
case OPERATOR_MUL_ASSIGN: return OS_TEXT("OPERATOR_MUL_ASSIGN");
case OPERATOR_DIV_ASSIGN: return OS_TEXT("OPERATOR_DIV_ASSIGN");
case OPERATOR_MOD_ASSIGN: return OS_TEXT("OPERATOR_MOD_ASSIGN");
case OPERATOR_LSHIFT_ASSIGN: return OS_TEXT("OPERATOR_LSHIFT_ASSIGN");
case OPERATOR_RSHIFT_ASSIGN: return OS_TEXT("OPERATOR_RSHIFT_ASSIGN");
case OPERATOR_POW_ASSIGN: return OS_TEXT("OPERATOR_POW_ASSIGN");
case OPERATOR_ASSIGN: return OS_TEXT("OPERATOR_ASSIGN");
case OPERATOR_RESERVED: return OS_TEXT("OPERATOR_RESERVED");
case OPERATOR_END: return OS_TEXT("OPERATOR_END");
// case PRE_PROCESSOR: return OS_TEXT("PRE_PROCESSOR");
case ERROR_TOKEN: return OS_TEXT("ERROR_TOKEN");
}
return OS_TEXT("UNKNOWN_TOKENTYPE");
}
OS::Core::Tokenizer::TokenData::TokenData(TextData * p_text_data, const String& p_str, TokenType p_type, int p_line, int p_pos): str(p_str)
{
text_data = p_text_data->retain();
ref_count = 1;
type = p_type;
line = p_line;
pos = p_pos;
// vec3 = NULL;
}
OS * OS::Core::Tokenizer::TokenData::getAllocator() const
{
return text_data->allocator;
}
OS::Core::Tokenizer::TokenData::~TokenData()
{
OS_ASSERT(ref_count == 0);
text_data->release();
}
OS::Core::Tokenizer::TokenData * OS::Core::Tokenizer::TokenData::retain()
{
ref_count++;
return this;
}
void OS::Core::Tokenizer::TokenData::release()
{
if(--ref_count <= 0){
OS_ASSERT(ref_count == 0);
OS * allocator = getAllocator();
this->~TokenData();
allocator->free(this);
}
}
OS_FLOAT OS::Core::Tokenizer::TokenData::getFloat() const
{
return float_value;
}
bool OS::Core::Tokenizer::TokenData::isTypeOf(TokenType token_type) const
{
if(type == token_type){
return true;
}
if(token_type == OS::Core::Tokenizer::SEPARATOR){
switch(type)
{
case OS::Core::Tokenizer::BEGIN_CODE_BLOCK: // {
case OS::Core::Tokenizer::END_CODE_BLOCK: // }
case OS::Core::Tokenizer::BEGIN_BRACKET_BLOCK: // (
case OS::Core::Tokenizer::END_BRACKET_BLOCK: // )
case OS::Core::Tokenizer::BEGIN_ARRAY_BLOCK: // [
case OS::Core::Tokenizer::END_ARRAY_BLOCK: // ]
case OS::Core::Tokenizer::CODE_SEPARATOR: // ;
case OS::Core::Tokenizer::PARAM_SEPARATOR: // ,
return true;
}
return false;
}
if(token_type == BINARY_OPERATOR){
switch(type)
{
case OS::Core::Tokenizer::PARAM_SEPARATOR:
case OS::Core::Tokenizer::OPERATOR_QUESTION:
case OS::Core::Tokenizer::OPERATOR_INDIRECT: // .
case OS::Core::Tokenizer::OPERATOR_CONCAT: // ..
case OS::Core::Tokenizer::OPERATOR_IN: // in
case OS::Core::Tokenizer::OPERATOR_ISPROTOTYPEOF:
case OS::Core::Tokenizer::OPERATOR_IS:
case OS::Core::Tokenizer::OPERATOR_LOGIC_AND: // &&
case OS::Core::Tokenizer::OPERATOR_LOGIC_OR: // ||
case OS::Core::Tokenizer::OPERATOR_LOGIC_PTR_EQ: // ===
case OS::Core::Tokenizer::OPERATOR_LOGIC_PTR_NE: // !==
case OS::Core::Tokenizer::OPERATOR_LOGIC_EQ: // ==
case OS::Core::Tokenizer::OPERATOR_LOGIC_NE: // !=
case OS::Core::Tokenizer::OPERATOR_LOGIC_GE: // >=
case OS::Core::Tokenizer::OPERATOR_LOGIC_LE: // <=
case OS::Core::Tokenizer::OPERATOR_LOGIC_GREATER: // >
case OS::Core::Tokenizer::OPERATOR_LOGIC_LESS: // <
case OS::Core::Tokenizer::OPERATOR_BIT_AND: // &
case OS::Core::Tokenizer::OPERATOR_BIT_OR: // |
case OS::Core::Tokenizer::OPERATOR_BIT_XOR: // ^
case OS::Core::Tokenizer::OPERATOR_BIT_NOT: // ~
case OS::Core::Tokenizer::OPERATOR_ADD: // +
case OS::Core::Tokenizer::OPERATOR_SUB: // -
case OS::Core::Tokenizer::OPERATOR_MUL: // *
case OS::Core::Tokenizer::OPERATOR_DIV: // /
case OS::Core::Tokenizer::OPERATOR_MOD: // %
case OS::Core::Tokenizer::OPERATOR_LSHIFT: // <<
case OS::Core::Tokenizer::OPERATOR_RSHIFT: // >>
case OS::Core::Tokenizer::OPERATOR_POW: // **
case OS::Core::Tokenizer::OPERATOR_BIT_AND_ASSIGN: // &=
case OS::Core::Tokenizer::OPERATOR_BIT_OR_ASSIGN: // |=
case OS::Core::Tokenizer::OPERATOR_BIT_XOR_ASSIGN: // ^=
case OS::Core::Tokenizer::OPERATOR_BIT_NOT_ASSIGN: // ~=
case OS::Core::Tokenizer::OPERATOR_ADD_ASSIGN: // +=
case OS::Core::Tokenizer::OPERATOR_SUB_ASSIGN: // -=
case OS::Core::Tokenizer::OPERATOR_MUL_ASSIGN: // *=
case OS::Core::Tokenizer::OPERATOR_DIV_ASSIGN: // /=
case OS::Core::Tokenizer::OPERATOR_MOD_ASSIGN: // %=
case OS::Core::Tokenizer::OPERATOR_LSHIFT_ASSIGN: // <<=
case OS::Core::Tokenizer::OPERATOR_RSHIFT_ASSIGN: // >>=
case OS::Core::Tokenizer::OPERATOR_POW_ASSIGN: // **=
case OS::Core::Tokenizer::OPERATOR_ASSIGN: // =
return true;
}
return false;
}
return false;
}
bool OS::Core::Tokenizer::operator_initialized = false;
OS::Core::Tokenizer::OperatorDesc OS::Core::Tokenizer::operator_desc[] =
{
{ OPERATOR_INDIRECT, OS_TEXT(".") },
{ OPERATOR_CONCAT, OS_TEXT("..") },
{ REST_ARGUMENTS, OS_TEXT("...") },
{ OPERATOR_RESERVED, OS_TEXT("->") },
{ OPERATOR_RESERVED, OS_TEXT("::") },
{ OPERATOR_LOGIC_AND, OS_TEXT("&&") },
{ OPERATOR_LOGIC_OR, OS_TEXT("||") },
{ OPERATOR_LOGIC_PTR_EQ, OS_TEXT("===") },
{ OPERATOR_LOGIC_PTR_NE, OS_TEXT("!==") },
{ OPERATOR_LOGIC_EQ, OS_TEXT("==") },
{ OPERATOR_LOGIC_NE, OS_TEXT("!=") },
{ OPERATOR_LOGIC_GE, OS_TEXT(">=") },
{ OPERATOR_LOGIC_LE, OS_TEXT("<=") },
{ OPERATOR_LOGIC_GREATER, OS_TEXT(">") },
{ OPERATOR_LOGIC_LESS, OS_TEXT("<") },
{ OPERATOR_LOGIC_NOT, OS_TEXT("!") },
{ OPERATOR_INC, OS_TEXT("++") },
{ OPERATOR_DEC, OS_TEXT("--") },
{ OPERATOR_QUESTION, OS_TEXT("?") },
{ OPERATOR_COLON, OS_TEXT(":") },
{ OPERATOR_LENGTH, OS_TEXT("#") },
{ OPERATOR_BIT_AND, OS_TEXT("&") },
{ OPERATOR_BIT_OR, OS_TEXT("|") },
{ OPERATOR_BIT_XOR, OS_TEXT("^") },
{ OPERATOR_BIT_NOT, OS_TEXT("~") },
{ OPERATOR_CONCAT, OS_TEXT("..") },
{ OPERATOR_ADD, OS_TEXT("+") },
{ OPERATOR_SUB, OS_TEXT("-") },
{ OPERATOR_MUL, OS_TEXT("*") },
{ OPERATOR_DIV, OS_TEXT("/") },
{ OPERATOR_MOD, OS_TEXT("%") },
{ OPERATOR_LSHIFT, OS_TEXT("<<") },
{ OPERATOR_RSHIFT, OS_TEXT(">>") },
{ OPERATOR_POW, OS_TEXT("**") },
{ OPERATOR_BIT_AND_ASSIGN, OS_TEXT("&=") },
{ OPERATOR_BIT_OR_ASSIGN, OS_TEXT("|=") },
{ OPERATOR_BIT_XOR_ASSIGN, OS_TEXT("^=") },
{ OPERATOR_BIT_NOT_ASSIGN, OS_TEXT("~=") },
{ OPERATOR_ADD_ASSIGN, OS_TEXT("+=") },
{ OPERATOR_SUB_ASSIGN, OS_TEXT("-=") },
{ OPERATOR_MUL_ASSIGN, OS_TEXT("*=") },
{ OPERATOR_DIV_ASSIGN, OS_TEXT("/=") },
{ OPERATOR_MOD_ASSIGN, OS_TEXT("%=") },
{ OPERATOR_LSHIFT_ASSIGN, OS_TEXT("<<=") },
{ OPERATOR_RSHIFT_ASSIGN, OS_TEXT(">>=") },
{ OPERATOR_POW_ASSIGN, OS_TEXT("**=") },
{ OPERATOR_ASSIGN, OS_TEXT("=") },
{ BEGIN_CODE_BLOCK, OS_TEXT("{") },
{ END_CODE_BLOCK, OS_TEXT("}") },
{ BEGIN_BRACKET_BLOCK, OS_TEXT("(") },
{ END_BRACKET_BLOCK, OS_TEXT(")") },
{ BEGIN_ARRAY_BLOCK, OS_TEXT("[") },
{ END_ARRAY_BLOCK, OS_TEXT("]") },
{ CODE_SEPARATOR, OS_TEXT(";") },
{ PARAM_SEPARATOR, OS_TEXT(",") }
};
const int OS::Core::Tokenizer::operator_count = sizeof(operator_desc) / sizeof(operator_desc[0]);
int OS::Core::Tokenizer::compareOperatorDesc(const void * a, const void * b)
{
const OperatorDesc * op0 = (const OperatorDesc*)a;
const OperatorDesc * op1 = (const OperatorDesc*)b;
return (int)OS_STRLEN(op1->name) - (int)OS_STRLEN(op0->name);
}
void OS::Core::Tokenizer::initOperatorsTable()
{
if(!operator_initialized){
::qsort(operator_desc, operator_count, sizeof(operator_desc[0]), Tokenizer::compareOperatorDesc);
operator_initialized = true;
}
}
OS::Core::Tokenizer::TextData::TextData(OS * p_allocator): filename(p_allocator)
{
allocator = p_allocator;
ref_count = 1;
}
OS::Core::Tokenizer::TextData::~TextData()
{
OS_ASSERT(!ref_count);
}
OS::Core::Tokenizer::TextData * OS::Core::Tokenizer::TextData::retain()
{
ref_count++;
return this;
}
void OS::Core::Tokenizer::TextData::release()
{
if(--ref_count <= 0){
OS_ASSERT(!ref_count);
OS * allocator = this->allocator;
allocator->vectorClear(lines);
this->~TextData();
allocator->free(this);
}
}
OS::Core::Tokenizer::Tokenizer(OS * p_allocator)
{
allocator = p_allocator;
initOperatorsTable();
settings.save_comments = false;
error = ERROR_NOTHING;
cur_line = 0;
cur_pos = 0;
text_data = new (allocator->malloc(sizeof(TextData) OS_DBG_FILEPOS)) TextData(allocator);
}
OS * OS::Core::Tokenizer::getAllocator()
{
return allocator;
}
OS::Core::Tokenizer::~Tokenizer()
{
OS * allocator = getAllocator();
for(int i = 0; i < tokens.count; i++){
TokenData * token = tokens[i];
token->release();
}
allocator->vectorClear(tokens);
// allocator->vectorClear(lines);
text_data->release();
}
OS::Core::Tokenizer::TokenData * OS::Core::Tokenizer::removeToken(int i)
{
TokenData * token = getToken(i);
getAllocator()->vectorRemoveAtIndex(tokens, i);
return token;
}
void OS::Core::Tokenizer::insertToken(int i, TokenData * token OS_DBG_FILEPOS_DECL)
{
getAllocator()->vectorInsertAtIndex(tokens, i, token OS_DBG_FILEPOS_PARAM);
}
bool OS::Core::Tokenizer::parseText(const OS_CHAR * text, int len, const String& filename)
{
OS_ASSERT(text_data->lines.count == 0);
OS * allocator = getAllocator();
// text_data->release();
// text_data = new (allocator->malloc(sizeof(TextData))) TextData(allocator);
text_data->filename = filename;
const OS_CHAR * str = text;
const OS_CHAR * str_end = str + len;
while(str < str_end)
{
#if 0
const OS_CHAR * line_end = OS_STRCHR(str, OS_TEXT('\n'));
if(line_end){
allocator->vectorAddItem(text_data->lines, String(allocator, str, line_end - str, false, true) OS_DBG_FILEPOS);
str = line_end+1;
}else{
allocator->vectorAddItem(text_data->lines, String(allocator, str, str_end - str, false, true) OS_DBG_FILEPOS);
break;
}
#else
const OS_CHAR * line_end = str;
for(; line_end < str_end && *line_end != OS_TEXT('\n'); line_end++);
allocator->vectorAddItem(text_data->lines, String(allocator, str, line_end - str, false, true) OS_DBG_FILEPOS);
str = line_end+1;
#endif
}
return parseLines();
}
void OS::Core::Tokenizer::TokenData::setFloat(OS_FLOAT value)
{
float_value = value;
}
OS::Core::Tokenizer::TokenData * OS::Core::Tokenizer::addToken(const String& str, TokenType type, int line, int pos OS_DBG_FILEPOS_DECL)
{
OS * allocator = getAllocator();
TokenData * token = new (allocator->malloc(sizeof(TokenData) OS_DBG_FILEPOS_PARAM)) TokenData(text_data, str, type, line, pos);
allocator->vectorAddItem(tokens, token OS_DBG_FILEPOS);
return token;
}
static bool isValidCharAfterNumber(const OS_CHAR * str)
{
return !*str || OS_IS_SPACE(*str) || OS_STRCHR(OS_TEXT("!@#$%^&*()-+={}[]\\|;:'\",<.>/?`~"), *str);
}
bool OS::Core::Tokenizer::parseFloat(const OS_CHAR *& str, OS_FLOAT& fval, bool parse_end_spaces)
{
if(Utils::parseFloat(str, fval)){
if(isValidCharAfterNumber(str)){
if(parse_end_spaces){
parseSpaces(str);
}
return true;
}
if(*str == OS_TEXT('f') && isValidCharAfterNumber(str+1)){
str++;
if(parse_end_spaces){
parseSpaces(str);
}
return true;
}
}
return false;
}
bool OS::Core::Tokenizer::parseLines()
{
OS * allocator = getAllocator();
cur_line = cur_pos = 0;
for(; cur_line < text_data->lines.count; cur_line++){
// parse line
const OS_CHAR * line_start = text_data->lines[cur_line].toChar();
const OS_CHAR * str = line_start;
cur_pos = 0;
for(;;){
// skip spaces
parseSpaces(str);
if(!*str){
break;
}
if(*str == OS_TEXT('"') || *str == OS_TEXT('\'')){ // begin string
StringBuffer s(allocator);
OS_CHAR closeChar = *str;
const OS_CHAR * token_start = str;
for(str++; *str && *str != closeChar;){
OS_CHAR c = *str++;
if(c == OS_TEXT('\\')){
switch(*str){
case OS_TEXT('r'): c = OS_TEXT('\r'); str++; break;
case OS_TEXT('n'): c = OS_TEXT('\n'); str++; break;
case OS_TEXT('t'): c = OS_TEXT('\t'); str++; break;
case OS_TEXT('\"'): c = OS_TEXT('\"'); str++; break;
case OS_TEXT('\''): c = OS_TEXT('\''); str++; break;
case OS_TEXT('\\'): c = OS_TEXT('\\'); str++; break;
//case OS_TEXT('x'):
default:
{
OS_INT val;
int maxVal = sizeof(OS_CHAR) == 2 ? 0xFFFF : 0xFF;
if(*str == OS_TEXT('x') || *str == OS_TEXT('X')){ // parse hex
str++;
if(!parseSimpleHex(str, val)){
cur_pos = str - line_start;
error = ERROR_CONST_STRING_ESCAPE_CHAR;
return false;
}
}else if(*str == OS_TEXT('0')){ // octal
if(!parseSimpleOctal(str, val)){
cur_pos = str - line_start;
error = ERROR_CONST_STRING_ESCAPE_CHAR;
return false;
}
}else if(*str >= OS_TEXT('1') && *str <= OS_TEXT('9')){
if(!parseSimpleDec(str, val)){
cur_pos = str - line_start;
error = ERROR_CONST_STRING_ESCAPE_CHAR;
return false;
}
}else{
val = c;
}
c = (OS_CHAR)(val <= maxVal ? val : maxVal);
}
break;
}
}
s.append(c);
}
if(*str != closeChar){
cur_pos = str - line_start;
error = ERROR_CONST_STRING;
return false;
}
str++;
addToken(s, STRING, cur_line, token_start - line_start OS_DBG_FILEPOS);
continue;
}
if(*str == OS_TEXT('/')){
if(str[1] == OS_TEXT('/')){ // begin line comment
if(settings.save_comments){
addToken(String(allocator, str), COMMENT_LINE, cur_line, str - line_start OS_DBG_FILEPOS);
}
break;
}
if(str[1] == OS_TEXT('*')){ // begin multi line comment
StringBuffer comment(allocator);
comment.append(str, 2);
int startLine = cur_line;
int startPos = str - line_start;
for(str += 2;;){
const OS_CHAR * end = OS_STRSTR(str, OS_TEXT("*/"));
if(end){
if(settings.save_comments){
comment.append(str, (int)(end+2 - str));
addToken(comment, COMMENT_MULTI_LINE, startLine, startPos OS_DBG_FILEPOS);
}
str = end + 2;
break;
}
if(cur_line >= text_data->lines.count){
error = ERROR_MULTI_LINE_COMMENT;
cur_pos = str - line_start;
return false;
}
if(settings.save_comments){
comment.append(str);
comment.append(OS_TEXT("\n")); // OS_TEXT("\r\n"));
}
str = line_start = text_data->lines[++cur_line].toChar();
}
continue;
}
}
if(*str == OS_TEXT('_') || *str == OS_TEXT('$') || *str == OS_TEXT('@')
|| (*str >= OS_TEXT('a') && *str <= OS_TEXT('z'))
|| (*str >= OS_TEXT('A') && *str <= OS_TEXT('Z')) )
{ // parse name
const OS_CHAR * name_start = str;
for(str++; *str; str++){
if(*str == OS_TEXT('_') || *str == OS_TEXT('$') || *str == OS_TEXT('@')
|| (*str >= OS_TEXT('a') && *str <= OS_TEXT('z'))
|| (*str >= OS_TEXT('A') && *str <= OS_TEXT('Z'))
|| (*str >= OS_TEXT('0') && *str <= OS_TEXT('9')) )
{
continue;
}
break;
}
String name = String(allocator, name_start, str - name_start);
TokenType type = NAME;
addToken(name, type, cur_line, name_start - line_start OS_DBG_FILEPOS);
continue;
}
// parse operator
if(0 && (*str == OS_TEXT('-') || *str == OS_TEXT('+')) && (str[1] >= OS_TEXT('0') && str[1] <= OS_TEXT('9'))){
int i = 0;
}else{
int i;
for(i = 0; i < operator_count; i++){
size_t len = OS_STRLEN(operator_desc[i].name);
if(OS_STRNCMP(str, operator_desc[i].name, len) == 0){
addToken(String(allocator, str, (int)len), operator_desc[i].type, cur_line, str - line_start OS_DBG_FILEPOS);
str += len;
break;
}
}
if(i < operator_count){
continue;
}
}
{
OS_FLOAT fval;
const OS_CHAR * token_start = str;
if(parseFloat(str, fval, true)){
TokenData * token = addToken(String(allocator, token_start, str - token_start, false, true), NUMBER, cur_line, token_start - line_start OS_DBG_FILEPOS);
token->setFloat(fval);
continue;
}
}
error = ERROR_SYNTAX;
cur_pos = str - line_start;
return false;
}
}
// PrintTokens();
return true;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::Compiler::ExpressionList::ExpressionList(OS * p_allocator)
{
allocator = p_allocator;
}
OS::Core::Compiler::ExpressionList::~ExpressionList()
{
allocator->vectorDeleteItems(*this);
allocator->vectorClear(*this);
}
bool OS::Core::Compiler::ExpressionList::isValue() const
{
return count > 0 && buf[count-1]->isValue();
}
bool OS::Core::Compiler::ExpressionList::isClear() const
{
return count <= 0 || buf[count-1]->isClear();
}
bool OS::Core::Compiler::ExpressionList::isWriteable() const
{
return count > 0 && buf[count-1]->isWriteable();
}
OS::Core::Compiler::Expression * OS::Core::Compiler::ExpressionList::add(Expression * exp OS_DBG_FILEPOS_DECL)
{
allocator->vectorAddItem(*this, exp OS_DBG_FILEPOS_PARAM);
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::ExpressionList::removeIndex(int i)
{
Expression * exp = (*this)[i];
allocator->vectorRemoveAtIndex(*this, i);
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::ExpressionList::removeLast()
{
return removeIndex(count-1);
}
void OS::Core::Compiler::ExpressionList::swap(ExpressionList& list)
{
OS_ASSERT(allocator == list.allocator);
Expression ** save_buf = buf;
int save_count = count;
int save_capacity = capacity;
buf = list.buf;
count = list.count;
capacity = list.capacity;
list.buf = save_buf;
list.count = save_count;
list.capacity = save_capacity;
}
// =====================================================================
OS::Core::Compiler::LocalVarDesc::LocalVarDesc()
{
up_count = 0;
up_scope_count = 0;
index = 0;
type = LOCAL_GENERIC;
}
OS::Core::Compiler::Expression::Expression(ExpressionType p_type, TokenData * p_token): list(p_token->getAllocator())
{
token = p_token->retain();
type = p_type;
ret_values = 0;
active_locals = 0;
}
OS::Core::Compiler::Expression::Expression(ExpressionType p_type, TokenData * p_token, Expression * e1 OS_DBG_FILEPOS_DECL): list(p_token->getAllocator())
{
token = p_token->retain();
type = p_type;
list.add(e1 OS_DBG_FILEPOS_PARAM);
ret_values = 0;
active_locals = 0;
}
OS::Core::Compiler::Expression::Expression(ExpressionType p_type, TokenData * p_token, Expression * e1, Expression * e2 OS_DBG_FILEPOS_DECL): list(p_token->getAllocator())
{
token = p_token->retain();
type = p_type;
list.add(e1 OS_DBG_FILEPOS_PARAM);
list.add(e2 OS_DBG_FILEPOS_PARAM);
ret_values = 0;
active_locals = 0;
}
OS::Core::Compiler::Expression::Expression(ExpressionType p_type, TokenData * p_token, Expression * e1, Expression * e2, Expression * e3 OS_DBG_FILEPOS_DECL): list(p_token->getAllocator())
{
token = p_token->retain();
type = p_type;
list.add(e1 OS_DBG_FILEPOS_PARAM);
list.add(e2 OS_DBG_FILEPOS_PARAM);
list.add(e3 OS_DBG_FILEPOS_PARAM);
ret_values = 0;
active_locals = 0;
}
OS::Core::Compiler::Expression::~Expression()
{
token->release();
}
bool OS::Core::Compiler::Expression::isConstValue() const
{
switch(type){
// case EXP_TYPE_CODE_LIST:
// return list.count ? list[list.count-1]->isValue() : false;
case EXP_TYPE_CONST_STRING:
case EXP_TYPE_CONST_NUMBER:
case EXP_TYPE_CONST_NULL:
case EXP_TYPE_CONST_TRUE:
case EXP_TYPE_CONST_FALSE:
// case EXP_TYPE_NAME:
// case EXP_TYPE_CALL:
OS_ASSERT(ret_values == 1);
return true;
}
return false;
}
bool OS::Core::Compiler::Expression::isValue() const
{
return ret_values > 0;
}
bool OS::Core::Compiler::Expression::isClear() const
{
return ret_values == 0;
}
bool OS::Core::Compiler::Expression::isWriteable() const
{
switch(type){
// case EXP_TYPE_CODE_LIST:
// return list.count ? list[list.count-1]->isWriteable() : false;
case EXP_TYPE_NAME:
case EXP_TYPE_INDIRECT:
case EXP_TYPE_CALL_DIM:
case EXP_TYPE_CALL_METHOD:
return true;
case EXP_TYPE_PARAMS:
for(int i = 0; i < list.count; i++){
if(list[i]->type == EXP_TYPE_PARAMS || !list[i]->isWriteable()){
return false;
}
}
return true;
}
return false;
}
bool OS::Core::Compiler::Expression::isOperator() const
{
return isBinaryOperator() || isUnaryOperator();
}
bool OS::Core::Compiler::Expression::isUnaryOperator() const
{
switch(type){
case EXP_TYPE_LOGIC_BOOL: // !!
case EXP_TYPE_LOGIC_NOT: // !
case EXP_TYPE_PLUS: // +
case EXP_TYPE_NEG: // -
case EXP_TYPE_LENGTH: // #
// case EXP_TYPE_INC: // ++
//case EXP_TYPE_DEC: // --
case EXP_TYPE_PRE_INC: // ++
case EXP_TYPE_PRE_DEC: // --
case EXP_TYPE_POST_INC: // ++
case EXP_TYPE_POST_DEC: // --
case EXP_TYPE_BIT_NOT: // ~
return true;
}
return false;
}
bool OS::Core::Compiler::Expression::isLogicOperator() const
{
switch(type){
case EXP_TYPE_LOGIC_BOOL: // !!
case EXP_TYPE_LOGIC_NOT: // !
case EXP_TYPE_LOGIC_AND: // &&
case EXP_TYPE_LOGIC_OR: // ||
case EXP_TYPE_LOGIC_PTR_EQ: // ===
case EXP_TYPE_LOGIC_PTR_NE: // !==
case EXP_TYPE_LOGIC_EQ: // ==
case EXP_TYPE_LOGIC_NE: // !=
case EXP_TYPE_LOGIC_GE: // >=
case EXP_TYPE_LOGIC_LE: // <=
case EXP_TYPE_LOGIC_GREATER: // >
case EXP_TYPE_LOGIC_LESS: // <
return true;
}
return false;
}
bool OS::Core::Compiler::Expression::isBinaryOperator() const
{
switch(type){
case EXP_TYPE_INDIRECT:
case EXP_TYPE_ASSIGN:
case EXP_TYPE_PARAMS:
case EXP_TYPE_QUESTION:
case EXP_TYPE_IN:
case EXP_TYPE_ISPROTOTYPEOF:
case EXP_TYPE_IS:
case EXP_TYPE_CONCAT: // ..
case EXP_TYPE_LOGIC_AND: // &&
case EXP_TYPE_LOGIC_OR: // ||
case EXP_TYPE_LOGIC_PTR_EQ: // ===
case EXP_TYPE_LOGIC_PTR_NE: // !==
case EXP_TYPE_LOGIC_EQ: // ==
case EXP_TYPE_LOGIC_NE: // !=
case EXP_TYPE_LOGIC_GE: // >=
case EXP_TYPE_LOGIC_LE: // <=
case EXP_TYPE_LOGIC_GREATER: // >
case EXP_TYPE_LOGIC_LESS: // <
case EXP_TYPE_BIT_AND: // &
case EXP_TYPE_BIT_OR: // |
case EXP_TYPE_BIT_XOR: // ^
case EXP_TYPE_BIT_AND_ASSIGN: // &=
case EXP_TYPE_BIT_OR_ASSIGN: // |=
case EXP_TYPE_BIT_XOR_ASSIGN: // ^=
case EXP_TYPE_BIT_NOT_ASSIGN: // ~=
case EXP_TYPE_ADD: // +
case EXP_TYPE_SUB: // -
case EXP_TYPE_MUL: // *
case EXP_TYPE_DIV: // /
case EXP_TYPE_MOD: // %
case EXP_TYPE_LSHIFT: // <<
case EXP_TYPE_RSHIFT: // >>
case EXP_TYPE_POW: // **
case EXP_TYPE_ADD_ASSIGN: // +=
case EXP_TYPE_SUB_ASSIGN: // -=
case EXP_TYPE_MUL_ASSIGN: // *=
case EXP_TYPE_DIV_ASSIGN: // /=
case EXP_TYPE_MOD_ASSIGN: // %=
case EXP_TYPE_LSHIFT_ASSIGN: // <<=
case EXP_TYPE_RSHIFT_ASSIGN: // >>=
case EXP_TYPE_POW_ASSIGN: // **=
return true;
}
return isAssignOperator();
}
bool OS::Core::Compiler::Expression::isAssignOperator() const
{
switch(type){
case EXP_TYPE_ASSIGN: // =
case EXP_TYPE_BIT_AND_ASSIGN: // &=
case EXP_TYPE_BIT_OR_ASSIGN: // |=
case EXP_TYPE_BIT_XOR_ASSIGN: // ^=
case EXP_TYPE_BIT_NOT_ASSIGN: // ~=
case EXP_TYPE_ADD_ASSIGN: // +=
case EXP_TYPE_SUB_ASSIGN: // -=
case EXP_TYPE_MUL_ASSIGN: // *=
case EXP_TYPE_DIV_ASSIGN: // /=
case EXP_TYPE_MOD_ASSIGN: // %=
case EXP_TYPE_LSHIFT_ASSIGN: // <<=
case EXP_TYPE_RSHIFT_ASSIGN: // >>=
case EXP_TYPE_POW_ASSIGN: // **=
return true;
}
return false;
}
void OS::Core::Compiler::Expression::debugPrint(StringBuffer& out, OS::Core::Compiler * compiler, int depth)
{
OS * allocator = getAllocator();
compiler->debugPrintSourceLine(out, token);
int i;
OS_CHAR * spaces = (OS_CHAR*)alloca(sizeof(OS_CHAR)*(depth*2+1));
for(i = 0; i < depth*2; i++){
spaces[i] = OS_TEXT(' ');
}
spaces[i] = OS_TEXT('\0');
const OS_CHAR * type_name;
switch(type){
case EXP_TYPE_NOP:
out += String::format(allocator, OS_TEXT("%snop\n"), spaces);
break;
case EXP_TYPE_CODE_LIST:
type_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, type_name);
for(i = 0; i < list.count; i++){
if(i > 0){
out += OS_TEXT("\n");
}
// OS_ASSERT(i+1 == list.count ? list[i]->ret_values == ret_values : list[i]->ret_values == 0);
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, type_name, ret_values);
break;
case EXP_TYPE_IF:
OS_ASSERT(list.count == 2 || list.count == 3);
out += String::format(allocator, OS_TEXT("%sbegin if\n"), spaces);
out += String::format(allocator, OS_TEXT("%s begin bool exp\n"), spaces);
list[0]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end bool exp\n"), spaces);
out += String::format(allocator, OS_TEXT("%s begin then\n"), spaces);
list[1]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end then\n"), spaces);
if(list.count == 3){
out += String::format(allocator, OS_TEXT("%s begin else\n"), spaces);
list[2]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end else\n"), spaces);
}
out += String::format(allocator, OS_TEXT("%send if ret values %d\n"), spaces, ret_values);
break;
case EXP_TYPE_QUESTION:
OS_ASSERT(list.count == 3);
out += String::format(allocator, OS_TEXT("%sbegin question\n"), spaces);
out += String::format(allocator, OS_TEXT("%s begin bool exp\n"), spaces);
list[0]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end bool exp\n"), spaces);
out += String::format(allocator, OS_TEXT("%s begin then value\n"), spaces);
list[1]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end then value\n"), spaces);
out += String::format(allocator, OS_TEXT("%s begin else value\n"), spaces);
list[2]->debugPrint(out, compiler, depth+2);
out += String::format(allocator, OS_TEXT("%s end else value\n"), spaces);
out += String::format(allocator, OS_TEXT("%send question ret values %d\n"), spaces, ret_values);
break;
case EXP_TYPE_CONST_NUMBER:
case EXP_TYPE_CONST_STRING:
{
const OS_CHAR * end = OS_TEXT("");
switch(token->type){
case Tokenizer::NUMBER: type_name = OS_TEXT("number "); break;
case Tokenizer::STRING: type_name = OS_TEXT("string \""); end = OS_TEXT("\""); break;
case Tokenizer::NAME: type_name = OS_TEXT("string \""); end = OS_TEXT("\""); break;
default: type_name = OS_TEXT("???"); break;
}
out += String::format(allocator, OS_TEXT("%spush const %s%s%s\n"), spaces, type_name, token->str.toChar(), end);
}
break;
case EXP_TYPE_CONST_NULL:
case EXP_TYPE_CONST_TRUE:
case EXP_TYPE_CONST_FALSE:
out += String::format(allocator, OS_TEXT("%s%s\n"), spaces, getExpName(type));
break;
case EXP_TYPE_NAME:
out += String::format(allocator, OS_TEXT("%sname %s\n"), spaces, token->str.toChar());
break;
case EXP_TYPE_PARAMS:
out += String::format(allocator, OS_TEXT("%sbegin params %d\n"), spaces, list.count);
for(i = 0; i < list.count; i++){
if(i > 0){
out += String::format(allocator, OS_TEXT("%s ,\n"), spaces);
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send params ret values %d\n"), spaces, ret_values);
break;
case EXP_TYPE_ARRAY:
out += String::format(allocator, OS_TEXT("%sbegin array %d\n"), spaces, list.count);
for(i = 0; i < list.count; i++){
if(i > 0){
out += String::format(allocator, OS_TEXT("%s ,\n"), spaces);
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send array\n"), spaces);
break;
case EXP_TYPE_OBJECT:
out += String::format(allocator, OS_TEXT("%sbegin object %d\n"), spaces, list.count);
for(i = 0; i < list.count; i++){
if(i > 0){
out += String::format(allocator, OS_TEXT("%s ,\n"), spaces);
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send object\n"), spaces);
break;
case EXP_TYPE_OBJECT_SET_BY_NAME:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin set by name\n"), spaces);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send set by name: [%s]\n"), spaces, token->str.toChar());
break;
case EXP_TYPE_OBJECT_SET_BY_INDEX:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin set by index\n"), spaces);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send set by index: [%d]\n"), spaces, (int)token->getFloat());
break;
case EXP_TYPE_OBJECT_SET_BY_EXP:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin set by exp\n"), spaces);
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send set by exp\n"), spaces);
break;
case EXP_TYPE_OBJECT_SET_BY_AUTO_INDEX:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin set auto index\n"), spaces);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send set auto index\n"), spaces);
break;
case EXP_TYPE_FUNCTION:
{
// OS_ASSERT(list.count >= 1);
Scope * scope = dynamic_cast<Scope*>(this);
OS_ASSERT(scope);
out += String::format(allocator, OS_TEXT("%sbegin function\n"), spaces);
if(scope->num_locals > 0){
out += String::format(allocator, OS_TEXT("%s begin locals, total %d\n"), spaces, scope->num_locals);
for(i = 0; i < scope->locals.count; i++){
out += String::format(allocator, OS_TEXT("%s %d %s%s\n"), spaces,
scope->locals[i].index,
scope->locals[i].name.toChar(),
i < scope->num_params ? OS_TEXT(" (param)") : OS_TEXT("")
);
}
out += String::format(allocator, OS_TEXT("%s end locals\n"), spaces);
}
for(i = 0; i < list.count; i++){
if(i > 0){
out += OS_TEXT("\n");
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send function\n"), spaces);
break;
}
case EXP_TYPE_SCOPE:
case EXP_TYPE_LOOP_SCOPE:
{
// OS_ASSERT(list.count >= 1);
Scope * scope = dynamic_cast<Scope*>(this);
OS_ASSERT(scope);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
if(scope->locals.count > 0){
out += String::format(allocator, OS_TEXT("%s begin locals %d\n"), spaces, scope->locals.count);
for(i = 0; i < scope->locals.count; i++){
out += String::format(allocator, OS_TEXT("%s %d %s%s\n"), spaces,
scope->locals[i].index,
scope->locals[i].name.toChar(),
i < scope->num_params ? OS_TEXT(" (param)") : OS_TEXT("")
);
}
out += String::format(allocator, OS_TEXT("%s end locals\n"), spaces);
}
for(i = 0; i < list.count; i++){
if(i > 0){
out += OS_TEXT("\n");
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, exp_name, ret_values);
break;
}
case EXP_TYPE_RETURN:
if(list.count > 0){
out += String::format(allocator, OS_TEXT("%sbegin return\n"), spaces);
for(i = 0; i < list.count; i++){
if(i > 0){
out += String::format(allocator, OS_TEXT("%s ,\n"), spaces);
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send return values %d\n"), spaces, ret_values);
}else{
out += String::format(allocator, OS_TEXT("%sreturn\n"), spaces);
}
break;
case EXP_TYPE_BREAK:
OS_ASSERT(list.count == 0);
out += String::format(allocator, OS_TEXT("%sbreak\n"), spaces);
break;
case EXP_TYPE_CONTINUE:
OS_ASSERT(list.count == 0);
out += String::format(allocator, OS_TEXT("%scontinue\n"), spaces);
break;
case EXP_TYPE_DEBUGGER:
OS_ASSERT(list.count == 0);
out += String::format(allocator, OS_TEXT("%sdebugger\n"), spaces);
break;
case EXP_TYPE_DEBUG_LOCALS:
out += String::format(allocator, OS_TEXT("%sbegin debug locals\n"), spaces);
for(i = 0; i < list.count; i++){
if(i > 0){
out += String::format(allocator, OS_TEXT("%s ,\n"), spaces);
}
list[i]->debugPrint(out, compiler, depth+1);
}
out += String::format(allocator, OS_TEXT("%send debug locals\n"), spaces);
break;
case EXP_TYPE_TAIL_CALL:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, OS::Core::Compiler::getExpName(type));
break;
case EXP_TYPE_TAIL_CALL_METHOD:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, OS::Core::Compiler::getExpName(type));
break;
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, OS::Core::Compiler::getExpName(type), ret_values);
break;
case EXP_TYPE_CALL_DIM:
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
case EXP_TYPE_GET_PROPERTY:
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
// case EXP_TYPE_GET_PROPERTY_DIM:
// case EXP_TYPE_SET_ENV_VAR_DIM:
case EXP_TYPE_EXTENDS:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, OS::Core::Compiler::getExpName(type), ret_values);
break;
case EXP_TYPE_DELETE:
OS_ASSERT(list.count == 2);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, OS::Core::Compiler::getExpName(type));
break;
case EXP_TYPE_CLONE:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, OS::Core::Compiler::getExpName(type));
break;
case EXP_TYPE_VALUE:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, OS::Core::Compiler::getExpName(type));
break;
case EXP_TYPE_POP_VALUE:
OS_ASSERT(list.count == 1);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, OS::Core::Compiler::getExpName(type));
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, OS::Core::Compiler::getExpName(type), ret_values);
break;
case EXP_TYPE_SUPER:
OS_ASSERT(list.count == 0);
out += String::format(allocator, OS_TEXT("%ssuper\n"), spaces);
break;
case EXP_TYPE_TYPE_OF:
case EXP_TYPE_VALUE_OF:
case EXP_TYPE_NUMBER_OF:
case EXP_TYPE_STRING_OF:
case EXP_TYPE_ARRAY_OF:
case EXP_TYPE_OBJECT_OF:
case EXP_TYPE_USERDATA_OF:
case EXP_TYPE_FUNCTION_OF:
case EXP_TYPE_PLUS: // +
case EXP_TYPE_NEG: // -
case EXP_TYPE_LENGTH: // #
case EXP_TYPE_LOGIC_BOOL: // !!
case EXP_TYPE_LOGIC_NOT: // !
case EXP_TYPE_BIT_NOT: // ~
case EXP_TYPE_PRE_INC: // ++
case EXP_TYPE_PRE_DEC: // --
case EXP_TYPE_POST_INC: // ++
case EXP_TYPE_POST_DEC: // --
{
OS_ASSERT(list.count == 1);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, exp_name);
break;
}
case EXP_TYPE_INDIRECT:
case EXP_TYPE_ASSIGN:
case EXP_TYPE_CONCAT: // ..
case EXP_TYPE_IN:
case EXP_TYPE_ISPROTOTYPEOF:
case EXP_TYPE_IS:
case EXP_TYPE_LOGIC_AND: // &&
case EXP_TYPE_LOGIC_OR: // ||
case EXP_TYPE_LOGIC_PTR_EQ: // ===
case EXP_TYPE_LOGIC_PTR_NE: // !==
case EXP_TYPE_LOGIC_EQ: // ==
case EXP_TYPE_LOGIC_NE: // !=
case EXP_TYPE_LOGIC_GE: // >=
case EXP_TYPE_LOGIC_LE: // <=
case EXP_TYPE_LOGIC_GREATER: // >
case EXP_TYPE_LOGIC_LESS: // <
case EXP_TYPE_BIT_AND: // &
case EXP_TYPE_BIT_OR: // |
case EXP_TYPE_BIT_XOR: // ^
// case EXP_TYPE_BIT_NOT: // ~
case EXP_TYPE_BIT_AND_ASSIGN: // &=
case EXP_TYPE_BIT_OR_ASSIGN: // |=
case EXP_TYPE_BIT_XOR_ASSIGN: // ^=
case EXP_TYPE_BIT_NOT_ASSIGN: // ~=
case EXP_TYPE_ADD: // +
case EXP_TYPE_SUB: // -
case EXP_TYPE_MUL: // *
case EXP_TYPE_DIV: // /
case EXP_TYPE_MOD: // %
case EXP_TYPE_LSHIFT: // <<
case EXP_TYPE_RSHIFT: // >>
case EXP_TYPE_POW: // **
case EXP_TYPE_ADD_ASSIGN: // +=
case EXP_TYPE_SUB_ASSIGN: // -=
case EXP_TYPE_MUL_ASSIGN: // *=
case EXP_TYPE_DIV_ASSIGN: // /=
case EXP_TYPE_MOD_ASSIGN: // %=
case EXP_TYPE_LSHIFT_ASSIGN: // <<=
case EXP_TYPE_RSHIFT_ASSIGN: // >>=
case EXP_TYPE_POW_ASSIGN: // **=
{
OS_ASSERT(list.count == 2);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, exp_name);
break;
}
case EXP_TYPE_BIN_OPERATOR_BY_LOCALS:
case EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER:
{
OS_ASSERT(list.count == 1);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s\n"), spaces, exp_name);
break;
}
case EXP_TYPE_NEW_LOCAL_VAR:
{
OS_ASSERT(list.count == 0);
String info = String::format(allocator, OS_TEXT("(%d %d%s)"),
local_var.index, local_var.up_count,
local_var.type == LOCAL_PARAM ? OS_TEXT(" param") : (local_var.type == LOCAL_TEMP ? OS_TEXT(" temp") : OS_TEXT("")));
out += String::format(allocator, OS_TEXT("%snew local var %s %s\n"), spaces, token->str.toChar(), info.toChar());
break;
}
case EXP_TYPE_GET_THIS:
case EXP_TYPE_GET_ARGUMENTS:
case EXP_TYPE_GET_REST_ARGUMENTS:
{
OS_ASSERT(list.count == 0);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%s%s\n"), spaces, exp_name);
break;
}
case EXP_TYPE_GET_LOCAL_VAR:
case EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE:
{
OS_ASSERT(list.count == 0);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
String info = String::format(allocator, OS_TEXT("(%d %d%s)"),
local_var.index, local_var.up_count,
local_var.type == LOCAL_PARAM ? OS_TEXT(" param") : (local_var.type == LOCAL_TEMP ? OS_TEXT(" temp") : OS_TEXT("")));
out += String::format(allocator, OS_TEXT("%s%s %s %s\n"), spaces, exp_name, token->str.toChar(), info.toChar());
break;
}
case EXP_TYPE_GET_ENV_VAR:
case EXP_TYPE_GET_ENV_VAR_AUTO_CREATE:
{
OS_ASSERT(list.count == 0);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%s%s %s\n"), spaces, exp_name, token->str.toChar());
break;
}
case EXP_TYPE_SET_LOCAL_VAR:
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS:
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER:
{
OS_ASSERT(list.count == 1);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
String info = String::format(allocator, OS_TEXT("(%d %d%s)"),
local_var.index, local_var.up_count,
local_var.type == LOCAL_PARAM ? OS_TEXT(" param") : (local_var.type == LOCAL_TEMP ? OS_TEXT(" temp") : OS_TEXT("")));
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s %s %s\n"), spaces, exp_name, token->str.toChar(), info.toChar());
break;
}
case EXP_TYPE_SET_ENV_VAR:
{
OS_ASSERT(list.count == 1);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s %s\n"), spaces, exp_name, token->str.toChar());
break;
}
case EXP_TYPE_SET_PROPERTY:
case EXP_TYPE_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
case EXP_TYPE_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
// case EXP_TYPE_SET_PROPERTY_DIM:
case EXP_TYPE_SET_DIM:
{
OS_ASSERT(list.count == 3);
const OS_CHAR * exp_name = OS::Core::Compiler::getExpName(type);
out += String::format(allocator, OS_TEXT("%sbegin %s\n"), spaces, exp_name);
list[0]->debugPrint(out, compiler, depth+1);
list[1]->debugPrint(out, compiler, depth+1);
list[2]->debugPrint(out, compiler, depth+1);
out += String::format(allocator, OS_TEXT("%send %s ret values %d\n"), spaces, exp_name, ret_values);
break;
}
}
}
// =====================================================================
int OS::Core::Compiler::cacheString(Table * strings_table, Vector<String>& strings, const String& str)
{
PropertyIndex index(str, PropertyIndex::KeepStringIndex());
Property * prop = strings_table->get(index);
if(prop){
OS_ASSERT(prop->value.type == OS_VALUE_TYPE_NUMBER);
return (int)prop->value.v.number;
}
prop = new (malloc(sizeof(Property) OS_DBG_FILEPOS)) Property(index);
prop->value = Value(strings_table->count);
allocator->core->addTableProperty(strings_table, prop);
allocator->vectorAddItem(strings, str OS_DBG_FILEPOS);
OS_ASSERT(strings_table->count == strings.count);
return strings_table->count-1;
}
int OS::Core::Compiler::cacheString(const String& str)
{
return cacheString(prog_strings_table, prog_strings, str);
}
int OS::Core::Compiler::cacheDebugString(const String& str)
{
return cacheString(prog_debug_strings_table, prog_debug_strings, str);
}
int OS::Core::Compiler::cacheNumber(OS_NUMBER num)
{
PropertyIndex index(num);
Property * prop = prog_numbers_table->get(index);
if(prop){
OS_ASSERT(prop->value.type == OS_VALUE_TYPE_NUMBER);
return (int)prop->value.v.number;
}
prop = new (malloc(sizeof(Property) OS_DBG_FILEPOS)) Property(index);
prop->value = Value(prog_numbers_table->count);
allocator->core->addTableProperty(prog_numbers_table, prop);
allocator->vectorAddItem(prog_numbers, num OS_DBG_FILEPOS);
OS_ASSERT(prog_numbers_table->count == prog_numbers.count);
return prog_numbers_table->count-1;
}
void OS::Core::Compiler::writeDebugInfo(Expression * exp)
{
if(prog_debug_info){
prog_num_debug_infos++;
prog_debug_info->writeUVariable(prog_opcodes->getPos());
prog_debug_info->writeUVariable(exp->token->line+1);
prog_debug_info->writeUVariable(exp->token->pos+1);
prog_debug_info->writeUVariable(cacheDebugString(exp->token->str));
}
}
void OS::Core::Compiler::writeJumpOpcode(int offs)
{
offs += 3;
if((int)(OS_INT8)offs == offs){
prog_opcodes->writeByte(Program::OP_JUMP_1);
prog_opcodes->writeInt8(offs);
prog_opcodes->writeInt8(0);
prog_opcodes->writeInt16(0);
return;
}
if((int)(OS_INT16)offs == offs){
prog_opcodes->writeByte(Program::OP_JUMP_2);
prog_opcodes->writeInt16(offs);
prog_opcodes->writeInt16(0);
return;
}
prog_opcodes->writeByte(Program::OP_JUMP_4);
prog_opcodes->writeInt32(offs);
}
void OS::Core::Compiler::fixJumpOpcode(StreamWriter * writer, int offs, int pos)
{
fixJumpOpcode(writer, offs, pos, Program::OP_JUMP_4);
}
void OS::Core::Compiler::fixJumpOpcode(StreamWriter * writer, int offs, int pos, int opcode)
{
struct Lib
{
static int getFastOpcode(int opcode, int type)
{
OS_ASSERT(type >= 1 && type <= 3);
switch(opcode){
case Program::OP_JUMP_4:
case Program::OP_IF_JUMP_4:
case Program::OP_IF_NOT_JUMP_4:
case Program::OP_LOGIC_AND_4:
case Program::OP_LOGIC_OR_4:
return opcode - 3 + type;
}
OS_ASSERT(false);
return Program::OP_UNKNOWN;
}
};
offs += 4;
if((int)(OS_INT8)offs == offs){
writer->writeByteAtPos(Lib::getFastOpcode(opcode, 1), pos);
writer->writeInt8AtPos(offs, pos+1);
return;
}
if((int)(OS_INT16)offs == offs){
writer->writeByteAtPos(Lib::getFastOpcode(opcode, 2), pos);
writer->writeInt16AtPos(offs, pos+1);
return;
}
writer->writeByteAtPos(Lib::getFastOpcode(opcode, 3), pos);
writer->writeInt32AtPos(offs, pos+1);
}
bool OS::Core::Compiler::writeOpcodes(Scope * scope, ExpressionList& list)
{
for(int i = 0; i < list.count; i++){
if(!writeOpcodes(scope, list[i])){
return false;
}
}
return true;
}
bool OS::Core::Compiler::writeOpcodes(Scope * scope, Expression * exp)
{
int i;
// writeDebugInfo(exp);
switch(exp->type){
default:
{
ExpressionType exp_type = exp->type;
OS_ASSERT(false);
break;
}
case EXP_TYPE_NOP:
break;
case EXP_TYPE_NEW_LOCAL_VAR:
break;
case EXP_TYPE_VALUE:
case EXP_TYPE_CODE_LIST:
if(!writeOpcodes(scope, exp->list)){
return false;
}
break;
case EXP_TYPE_CONST_NUMBER:
{
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
OS_NUMBER number = (OS_NUMBER)exp->token->getFloat();
if(number == 1.0f){
prog_opcodes->writeByte(Program::OP_PUSH_ONE);
}else{
int i = cacheNumber(number);
if(i <= 255){
prog_opcodes->writeByte(Program::OP_PUSH_NUMBER_1);
prog_opcodes->writeByte(i);
}else{
prog_opcodes->writeByte(Program::OP_PUSH_NUMBER_BY_AUTO_INDEX);
prog_opcodes->writeUVariable(i);
}
}
break;
}
case EXP_TYPE_CONST_STRING:
{
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
int i = cacheString(exp->token->str);
if(i <= 255){
prog_opcodes->writeByte(Program::OP_PUSH_STRING_1);
prog_opcodes->writeByte(i);
}else{
prog_opcodes->writeByte(Program::OP_PUSH_STRING_BY_AUTO_INDEX);
prog_opcodes->writeUVariable(i);
}
break;
}
case EXP_TYPE_CONST_NULL:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_NULL);
break;
case EXP_TYPE_CONST_TRUE:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_TRUE);
break;
case EXP_TYPE_CONST_FALSE:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_FALSE);
break;
case EXP_TYPE_FUNCTION:
{
Scope * scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(scope);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_FUNCTION);
int prog_func_index = scope->prog_func_index; // prog_functions.indexOf(scope);
OS_ASSERT(prog_func_index >= 0);
prog_opcodes->writeUVariable(prog_func_index);
allocator->vectorReserveCapacity(scope->locals_compiled, scope->num_locals OS_DBG_FILEPOS);
scope->locals_compiled.count = scope->num_locals;
scope->opcodes_pos = prog_opcodes->getPos();
if(!writeOpcodes(scope, exp->list)){
return false;
}
prog_opcodes->writeByte(Program::OP_RETURN_AUTO);
scope->opcodes_size = prog_opcodes->getPos() - scope->opcodes_pos;
for(i = 0; i < scope->locals.count; i++){
Scope::LocalVar& var = scope->locals[i];
Scope::LocalVarCompiled& var_scope = scope->locals_compiled[var.index];
var_scope.cached_name_index = cacheString(var.name);
var_scope.start_code_pos = scope->opcodes_pos;
var_scope.end_code_pos = prog_opcodes->getPos();
}
break;
}
case EXP_TYPE_SCOPE:
case EXP_TYPE_LOOP_SCOPE:
{
Scope * scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(scope);
int start_code_pos = prog_opcodes->getPos();
if(!writeOpcodes(scope, exp->list)){
return false;
}
if(exp->type == EXP_TYPE_LOOP_SCOPE){
// prog_opcodes->writeByte(Program::OP_JUMP);
// prog_opcodes->writeInt32(start_code_pos - prog_opcodes->getPos() - sizeof(OS_INT32));
writeJumpOpcode(start_code_pos - prog_opcodes->getPos() - sizeof(OS_INT32));
scope->fixLoopBreaks(this, start_code_pos, prog_opcodes->getPos(), prog_opcodes);
}else{
OS_ASSERT(scope->loop_breaks.count == 0);
}
for(i = 0; i < scope->locals.count; i++){
Scope::LocalVar& var = scope->locals[i];
Scope::LocalVarCompiled& var_scope = scope->function->locals_compiled[var.index];
var_scope.cached_name_index = cacheString(var.name);
var_scope.start_code_pos = start_code_pos;
var_scope.end_code_pos = prog_opcodes->getPos();
}
break;
}
case EXP_TYPE_IF:
{
OS_ASSERT(exp->list.count == 2 || exp->list.count == 3);
int if_opcode;
if(exp->list[0]->type == EXP_TYPE_LOGIC_NOT){
OS_ASSERT(exp->list[0]->list.count == 1);
if(!writeOpcodes(scope, exp->list[0]->list)){
return false;
}
if_opcode = Program::OP_IF_JUMP_4;
}else{
if(!writeOpcodes(scope, exp->list[0])){
return false;
}
if_opcode = Program::OP_IF_NOT_JUMP_4;
}
writeDebugInfo(exp);
int if_jump_pos = prog_opcodes->getPos();
prog_opcodes->writeByte(if_opcode);
prog_opcodes->writeInt32(0);
if(!writeOpcodes(scope, exp->list[1])){
return false;
}
int if_jump_to = prog_opcodes->getPos();
if(exp->list.count == 3 && exp->list[2]->list.count > 0){
int jump_pos = prog_opcodes->getPos();
prog_opcodes->writeByte(Program::OP_JUMP_4);
prog_opcodes->writeInt32(0);
if_jump_to = prog_opcodes->getPos();
if(!writeOpcodes(scope, exp->list[2])){
return false;
}
// prog_opcodes->writeInt32AtPos(prog_opcodes->getPos() - jump_pos - sizeof(OS_BYTE)*5, jump_pos);
fixJumpOpcode(prog_opcodes, prog_opcodes->getPos() - jump_pos - sizeof(OS_BYTE)*5, jump_pos, Program::OP_JUMP_4);
}
// prog_opcodes->writeInt32AtPos(if_jump_to - if_jump_pos - sizeof(OS_BYTE)*5, if_jump_pos);
fixJumpOpcode(prog_opcodes, if_jump_to - if_jump_pos - sizeof(OS_BYTE)*5, if_jump_pos, if_opcode);
break;
}
case EXP_TYPE_QUESTION:
{
OS_ASSERT(exp->list.count == 3);
int if_opcode;
if(exp->list[0]->type == EXP_TYPE_LOGIC_NOT){
OS_ASSERT(exp->list[0]->list.count == 1);
if(!writeOpcodes(scope, exp->list[0]->list)){
return false;
}
if_opcode = Program::OP_IF_JUMP_4;
}else{
if(!writeOpcodes(scope, exp->list[0])){
return false;
}
if_opcode = Program::OP_IF_NOT_JUMP_4;
}
writeDebugInfo(exp);
int if_jump_pos = prog_opcodes->getPos();
prog_opcodes->writeByte(if_opcode);
prog_opcodes->writeInt32(0);
if(!writeOpcodes(scope, exp->list[1])){
return false;
}
int jump_pos = prog_opcodes->getPos();
prog_opcodes->writeByte(Program::OP_JUMP_4);
prog_opcodes->writeInt32(0);
int if_jump_to = prog_opcodes->getPos();
if(!writeOpcodes(scope, exp->list[2])){
return false;
}
// prog_opcodes->writeInt32AtPos(prog_opcodes->getPos() - jump_pos - sizeof(OS_BYTE)*5, jump_pos);
fixJumpOpcode(prog_opcodes, prog_opcodes->getPos() - jump_pos - sizeof(OS_BYTE)*5, jump_pos, Program::OP_JUMP_4);
// prog_opcodes->writeInt32AtPos(if_jump_to - if_jump_pos - sizeof(OS_BYTE)*5, if_jump_pos);
fixJumpOpcode(prog_opcodes, if_jump_to - if_jump_pos - sizeof(OS_BYTE)*5, if_jump_pos, if_opcode);
break;
}
case EXP_TYPE_LOGIC_AND: // &&
case EXP_TYPE_LOGIC_OR: // ||
{
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list[0])){
return false;
}
writeDebugInfo(exp);
int opcode = Program::getOpcodeType(exp->type);
int op_jump_pos = prog_opcodes->getPos();
prog_opcodes->writeByte(opcode);
prog_opcodes->writeInt32(0);
if(!writeOpcodes(scope, exp->list[1])){
return false;
}
int op_jump_to = prog_opcodes->getPos();
// prog_opcodes->writeInt32AtPos(op_jump_to - op_jump_pos - sizeof(OS_BYTE)*5, op_jump_pos);
fixJumpOpcode(prog_opcodes, op_jump_to - op_jump_pos - sizeof(OS_BYTE)*5, op_jump_pos, opcode);
break;
}
case EXP_TYPE_EXTENDS:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_EXTENDS);
break;
case EXP_TYPE_CLONE:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_CLONE);
break;
case EXP_TYPE_DELETE:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_DELETE_PROP);
break;
case EXP_TYPE_ARRAY:
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_NEW_ARRAY);
prog_opcodes->writeByte(exp->list.count > 255 ? 256 : exp->list.count);
if(!writeOpcodes(scope, exp->list)){
return false;
}
break;
case EXP_TYPE_OBJECT:
// OS_ASSERT(exp->list.count >= 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_NEW_OBJECT);
if(!writeOpcodes(scope, exp->list)){
return false;
}
break;
case EXP_TYPE_OBJECT_SET_BY_AUTO_INDEX:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_OBJECT_SET_BY_AUTO_INDEX);
break;
case EXP_TYPE_OBJECT_SET_BY_EXP:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_OBJECT_SET_BY_EXP);
break;
case EXP_TYPE_OBJECT_SET_BY_INDEX:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_OBJECT_SET_BY_INDEX);
// prog_opcodes->writeInt64(exp->token->getInt());
prog_opcodes->writeUVariable(cacheNumber((OS_NUMBER)exp->token->getFloat()));
break;
case EXP_TYPE_OBJECT_SET_BY_NAME:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_OBJECT_SET_BY_NAME);
prog_opcodes->writeUVariable(cacheString(exp->token->str));
break;
case EXP_TYPE_GET_ENV_VAR:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_ENV_VAR);
prog_opcodes->writeUVariable(cacheString(exp->token->str));
break;
case EXP_TYPE_GET_ENV_VAR_AUTO_CREATE:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_ENV_VAR_AUTO_CREATE);
prog_opcodes->writeUVariable(cacheString(exp->token->str));
break;
case EXP_TYPE_SET_ENV_VAR:
OS_ASSERT(exp->list.count > 0);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SET_ENV_VAR);
prog_opcodes->writeUVariable(cacheString(exp->token->str));
break;
case EXP_TYPE_GET_THIS:
case EXP_TYPE_GET_ARGUMENTS:
case EXP_TYPE_GET_REST_ARGUMENTS:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
break;
case EXP_TYPE_GET_LOCAL_VAR:
OS_ASSERT(exp->list.count == 0);
if(!exp->local_var.up_count){
writeDebugInfo(exp);
if(exp->local_var.index <= 255){
prog_opcodes->writeByte(Program::OP_PUSH_LOCAL_VAR_1);
prog_opcodes->writeByte(exp->local_var.index);
}else{
prog_opcodes->writeByte(Program::OP_PUSH_LOCAL_VAR_BY_AUTO_INDEX);
prog_opcodes->writeUVariable(exp->local_var.index);
}
}else{
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_UP_LOCAL_VAR);
prog_opcodes->writeUVariable(exp->local_var.index);
prog_opcodes->writeByte(exp->local_var.up_count);
}
break;
case EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE:
OS_ASSERT(exp->list.count == 0);
if(!exp->local_var.up_count){
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_LOCAL_VAR_AUTO_CREATE);
prog_opcodes->writeUVariable(exp->local_var.index);
}else{
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_UP_LOCAL_VAR_AUTO_CREATE);
prog_opcodes->writeUVariable(exp->local_var.index);
prog_opcodes->writeByte(exp->local_var.up_count);
}
break;
case EXP_TYPE_SET_LOCAL_VAR:
OS_ASSERT(exp->list.count > 0);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
if(!exp->local_var.up_count){
if(exp->local_var.index <= 255){
prog_opcodes->writeByte(Program::OP_SET_LOCAL_VAR_1);
prog_opcodes->writeByte(exp->local_var.index);
}else{
prog_opcodes->writeByte(Program::OP_SET_LOCAL_VAR);
prog_opcodes->writeUVariable(exp->local_var.index);
}
}else{
prog_opcodes->writeByte(Program::OP_SET_UP_LOCAL_VAR);
prog_opcodes->writeUVariable(exp->local_var.index);
prog_opcodes->writeByte(exp->local_var.up_count);
}
break;
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS:
{
OS_ASSERT(exp->list.count == 1);
OS_ASSERT(!exp->local_var.up_count);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_BIN_OPERATOR_BY_LOCALS);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS);
Expression * exp_binary = exp->list[0]->list[0];
Expression * exp1 = exp_binary->list[0];
Expression * exp2 = exp_binary->list[1];
prog_opcodes->writeByte(Program::getOpcodeType(exp_binary->type));
prog_opcodes->writeByte(exp1->local_var.index);
prog_opcodes->writeByte(exp2->local_var.index);
prog_opcodes->writeUVariable(exp->local_var.index);
break;
}
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER:
{
OS_ASSERT(exp->list.count == 1);
OS_ASSERT(!exp->local_var.up_count);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER);
writeDebugInfo(exp);
Expression * exp_binary = exp->list[0]->list[0];
Expression * exp1 = exp_binary->list[0];
Expression * exp2 = exp_binary->list[1];
int number_index = cacheNumber((OS_NUMBER)exp2->token->getFloat());
if(number_index <= 255 && exp->local_var.index <= 255){
prog_opcodes->writeByte(Program::OP_SET_LOCAL_VAR_1_BY_BIN_OPERATOR_LOCAL_AND_NUMBER);
prog_opcodes->writeByte(Program::getOpcodeType(exp_binary->type));
prog_opcodes->writeByte(exp1->local_var.index);
prog_opcodes->writeByte(number_index);
prog_opcodes->writeByte(exp->local_var.index);
}else{
prog_opcodes->writeByte(Program::OP_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER);
prog_opcodes->writeByte(Program::getOpcodeType(exp_binary->type));
prog_opcodes->writeByte(exp1->local_var.index);
prog_opcodes->writeUVariable(number_index);
prog_opcodes->writeUVariable(exp->local_var.index);
}
break;
}
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
{
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_PARAMS);
bool is_super_call = exp->list[0]->type == EXP_TYPE_SUPER;
if(is_super_call){
prog_opcodes->writeByte(Program::OP_PUSH_NULL); // func
}else if(!writeOpcodes(scope, exp->list[0])){
return false;
}
// writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_PUSH_NULL); // this
if(!writeOpcodes(scope, exp->list[1])){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(is_super_call ? Program::OP_SUPER_CALL : Program::getOpcodeType(exp->type));
prog_opcodes->writeByte(exp->list[1]->ret_values); // params number
prog_opcodes->writeByte(exp->ret_values);
break;
}
case EXP_TYPE_TAIL_CALL:
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_PARAMS);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
prog_opcodes->writeByte(exp->list[1]->ret_values); // params number
break;
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_PARAMS);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
prog_opcodes->writeByte(exp->list[1]->ret_values-1); // params number
prog_opcodes->writeByte(exp->ret_values);
break;
case EXP_TYPE_TAIL_CALL_METHOD:
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_PARAMS);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
prog_opcodes->writeByte(exp->list[1]->ret_values-1); // params number
break;
case EXP_TYPE_GET_PROPERTY:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_PROPERTY);
prog_opcodes->writeByte(exp->ret_values);
break;
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
{
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_GET_THIS);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_CONST_STRING);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_THIS_PROPERTY_BY_STRING);
prog_opcodes->writeByte(exp->ret_values);
prog_opcodes->writeUVariable(cacheString(exp->list[1]->token->str));
break;
}
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->local_var.up_count);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[1]->local_var.up_count);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_PROPERTY_BY_LOCALS);
prog_opcodes->writeByte(exp->ret_values);
prog_opcodes->writeByte(exp->list[0]->local_var.index);
prog_opcodes->writeByte(exp->list[1]->local_var.index);
break;
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
OS_ASSERT(exp->list.count == 2);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->local_var.up_count);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_CONST_NUMBER);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_PROPERTY_BY_LOCAL_AND_NUMBER);
prog_opcodes->writeByte(exp->ret_values);
prog_opcodes->writeByte(exp->list[0]->local_var.index);
prog_opcodes->writeUVariable(cacheNumber((OS_NUMBER)exp->list[1]->token->getFloat()));
break;
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_PROPERTY_AUTO_CREATE);
prog_opcodes->writeByte(exp->ret_values);
break;
case EXP_TYPE_SET_PROPERTY:
OS_ASSERT(exp->list.count == 3);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SET_PROPERTY);
break;
case EXP_TYPE_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
OS_ASSERT(exp->list.count == 3);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE && !exp->list[1]->local_var.up_count);
OS_ASSERT(exp->list[2]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[2]->local_var.up_count);
writeOpcodes(scope, exp->list[0]);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SET_PROPERTY_BY_LOCALS_AUTO_CREATE);
prog_opcodes->writeByte(exp->list[1]->local_var.index);
prog_opcodes->writeByte(exp->list[2]->local_var.index);
break;
case EXP_TYPE_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
OS_ASSERT(exp->list.count == 3);
OS_ASSERT(exp->list[0]->type == EXP_TYPE_GET_PROPERTY_BY_LOCALS && exp->list[0]->list.count == 2);
OS_ASSERT(exp->list[0]->list[0]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->list[0]->local_var.up_count);
OS_ASSERT(exp->list[0]->list[1]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->list[1]->local_var.up_count);
OS_ASSERT(exp->list[1]->type == EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE && !exp->list[1]->local_var.up_count);
OS_ASSERT(exp->list[2]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[2]->local_var.up_count);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE);
prog_opcodes->writeByte(exp->list[0]->list[0]->local_var.index);
prog_opcodes->writeByte(exp->list[0]->list[1]->local_var.index);
prog_opcodes->writeByte(exp->list[1]->local_var.index);
prog_opcodes->writeByte(exp->list[2]->local_var.index);
break;
case EXP_TYPE_SET_DIM:
OS_ASSERT(exp->list.count == 3);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SET_DIM);
prog_opcodes->writeByte(exp->list[2]->list.count); // params
break;
case EXP_TYPE_PARAMS:
if(!writeOpcodes(scope, exp->list)){
return false;
}
break;
case EXP_TYPE_RETURN:
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_RETURN);
prog_opcodes->writeByte(exp->ret_values);
break;
case EXP_TYPE_BREAK:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
scope->addLoopBreak(prog_opcodes->getPos(), Scope::LOOP_BREAK);
prog_opcodes->writeByte(Program::OP_JUMP_4);
prog_opcodes->writeInt32(0);
break;
case EXP_TYPE_CONTINUE:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
scope->addLoopBreak(prog_opcodes->getPos(), Scope::LOOP_CONTINUE);
prog_opcodes->writeByte(Program::OP_JUMP_4);
prog_opcodes->writeInt32(0);
break;
case EXP_TYPE_DEBUGGER:
{
OS_ASSERT(exp->list.count == 0);
prog_opcodes->writeByte(Program::OP_DEBUGGER);
prog_opcodes->writeUVariable(exp->token->line + 1);
prog_opcodes->writeUVariable(exp->token->pos + 1);
prog_opcodes->writeUVariable(OS_DEBUGGER_SAVE_NUM_LINES);
Core::String empty(allocator);
for(int i = 0; i < OS_DEBUGGER_SAVE_NUM_LINES; i++){
int j = exp->token->line - OS_DEBUGGER_SAVE_NUM_LINES/2 + i;
if(j >= 0 && j < exp->token->text_data->lines.count){
prog_opcodes->writeUVariable(cacheString(exp->token->text_data->lines[j]));
}else{
prog_opcodes->writeUVariable(cacheString(empty));
}
}
break;
}
case EXP_TYPE_DEBUG_LOCALS:
if(!writeOpcodes(scope, exp->list)){
return false;
}
break;
case EXP_TYPE_POP_VALUE:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
// writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_POP);
break;
case EXP_TYPE_SUPER:
OS_ASSERT(exp->list.count == 0);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_SUPER);
break;
case EXP_TYPE_TYPE_OF:
case EXP_TYPE_VALUE_OF:
case EXP_TYPE_NUMBER_OF:
case EXP_TYPE_STRING_OF:
case EXP_TYPE_ARRAY_OF:
case EXP_TYPE_OBJECT_OF:
case EXP_TYPE_USERDATA_OF:
case EXP_TYPE_FUNCTION_OF:
case EXP_TYPE_LOGIC_BOOL:
case EXP_TYPE_LOGIC_NOT:
case EXP_TYPE_BIT_NOT:
case EXP_TYPE_PLUS:
case EXP_TYPE_NEG:
case EXP_TYPE_LENGTH:
OS_ASSERT(exp->list.count == 1);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
break;
case EXP_TYPE_CONCAT:
case EXP_TYPE_IN:
case EXP_TYPE_ISPROTOTYPEOF:
case EXP_TYPE_IS:
// case EXP_TYPE_LOGIC_AND:
// case EXP_TYPE_LOGIC_OR:
case EXP_TYPE_LOGIC_PTR_EQ:
case EXP_TYPE_LOGIC_PTR_NE:
case EXP_TYPE_LOGIC_EQ:
case EXP_TYPE_LOGIC_NE:
case EXP_TYPE_LOGIC_GE:
case EXP_TYPE_LOGIC_LE:
case EXP_TYPE_LOGIC_GREATER:
case EXP_TYPE_LOGIC_LESS:
case EXP_TYPE_BIT_AND:
case EXP_TYPE_BIT_OR:
case EXP_TYPE_BIT_XOR:
case EXP_TYPE_ADD:
case EXP_TYPE_SUB:
case EXP_TYPE_MUL:
case EXP_TYPE_DIV:
case EXP_TYPE_MOD:
case EXP_TYPE_LSHIFT:
case EXP_TYPE_RSHIFT:
case EXP_TYPE_POW:
OS_ASSERT(exp->list.count == 2);
if(!writeOpcodes(scope, exp->list)){
return false;
}
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::getOpcodeType(exp->type));
break;
case EXP_TYPE_BIN_OPERATOR_BY_LOCALS:
{
OS_ASSERT(exp->list.count == 1);
OS_ASSERT(exp->list[0]->isBinaryOperator());
OS_ASSERT(exp->list[0]->list[0]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->list[0]->local_var.up_count);
OS_ASSERT(exp->list[0]->list[1]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->list[1]->local_var.up_count);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_BIN_OPERATOR_BY_LOCALS);
Expression * exp_binary = exp->list[0];
Expression * exp1 = exp_binary->list[0];
Expression * exp2 = exp_binary->list[1];
prog_opcodes->writeByte(Program::getOpcodeType(exp_binary->type));
prog_opcodes->writeByte(exp1->local_var.index);
prog_opcodes->writeByte(exp2->local_var.index);
break;
}
case EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER:
{
OS_ASSERT(exp->list.count == 1);
OS_ASSERT(exp->list[0]->isBinaryOperator());
OS_ASSERT(exp->list[0]->list[0]->type == EXP_TYPE_GET_LOCAL_VAR && !exp->list[0]->list[0]->local_var.up_count);
OS_ASSERT(exp->list[0]->list[1]->type == EXP_TYPE_CONST_NUMBER);
writeDebugInfo(exp);
prog_opcodes->writeByte(Program::OP_BIN_OPERATOR_BY_LOCAL_AND_NUMBER);
Expression * exp_binary = exp->list[0];
Expression * exp1 = exp_binary->list[0];
Expression * exp2 = exp_binary->list[1];
prog_opcodes->writeByte(Program::getOpcodeType(exp_binary->type));
prog_opcodes->writeByte(exp1->local_var.index);
prog_opcodes->writeUVariable(cacheNumber((OS_NUMBER)exp2->token->getFloat()));
break;
}
}
return true;
}
// =====================================================================
OS::Core::Compiler::Scope::Scope(Scope * p_parent, ExpressionType type, TokenData * token): Expression(type, token)
{
OS_ASSERT(type == EXP_TYPE_FUNCTION || type == EXP_TYPE_SCOPE || type == EXP_TYPE_LOOP_SCOPE);
parent = p_parent;
function = type == EXP_TYPE_FUNCTION ? this : parent->function;
num_params = 0;
num_locals = 0;
max_up_count = 0;
func_depth = 0;
func_index = 0;
num_local_funcs = 0;
prog_func_index = -1;
parser_started = false;
}
OS::Core::Compiler::Scope::~Scope()
{
getAllocator()->vectorClear(locals);
getAllocator()->vectorClear(locals_compiled);
getAllocator()->vectorClear(loop_breaks);
}
OS::Core::Compiler::Scope::LocalVar::LocalVar(const String& p_name, int p_index): name(p_name)
{
index = p_index;
}
OS::Core::Compiler::Scope::LocalVarCompiled::LocalVarCompiled()
{
cached_name_index = -1;
start_code_pos = -1;
end_code_pos = -1;
}
bool OS::Core::Compiler::Scope::addLoopBreak(int pos, ELoopBreakType type)
{
Scope * scope = this;
for(; scope; scope = scope->parent){
if(scope->type == EXP_TYPE_LOOP_SCOPE){
break;
}
}
if(!scope){
return false;
}
LoopBreak loop_break;
loop_break.pos = pos;
loop_break.type = type;
getAllocator()->vectorAddItem(scope->loop_breaks, loop_break OS_DBG_FILEPOS);
return true;
}
void OS::Core::Compiler::Scope::fixLoopBreaks(Compiler * compiler, int scope_start_pos, int scope_end_pos, StreamWriter * writer)
{
for(int i = 0; i < loop_breaks.count; i++){
LoopBreak& loop_break = loop_breaks[i];
if(loop_break.type == LOOP_BREAK){
int offs = scope_end_pos - loop_break.pos - sizeof(OS_BYTE)*5;
// writer->writeInt32AtPos(offs, loop_break.pos);
compiler->fixJumpOpcode(writer, offs, loop_break.pos);
}else{
int offs = scope_start_pos - loop_break.pos - sizeof(OS_BYTE)*5;
// writer->writeInt32AtPos(offs, loop_break.pos);
compiler->fixJumpOpcode(writer, offs, loop_break.pos);
}
}
}
void OS::Core::Compiler::Scope::addStdVars()
{
OS_ASSERT(ENV_VAR_INDEX == 0);
Core::Strings * strings = getAllocator()->core->strings;
// don't change following order
addLocalVar(strings->var_env);
#ifdef OS_GLOBAL_VAR_ENABLED
OS_ASSERT(GLOBALS_VAR_INDEX == 1);
addLocalVar(strings->var_globals);
#endif
}
void OS::Core::Compiler::Scope::addLocalVar(const String& name)
{
OS * allocator = getAllocator();
LocalVar local_var(name, function->num_locals);
allocator->vectorAddItem(locals, local_var OS_DBG_FILEPOS);
function->num_locals++;
}
void OS::Core::Compiler::Scope::addLocalVar(const String& name, LocalVarDesc& local_var)
{
local_var.index = function->num_locals;
local_var.up_count = 0;
local_var.type = LOCAL_GENERIC;
addLocalVar(name);
}
// =====================================================================
OS::Core::Compiler::Compiler(Tokenizer * p_tokenizer)
: expect_token(p_tokenizer->getAllocator())
{
allocator = p_tokenizer->getAllocator();
tokenizer = p_tokenizer;
error = ERROR_NOTHING;
error_token = NULL;
expect_token_type = Tokenizer::NOTHING;
recent_token = NULL;
next_token_index = 0;
recent_printed_text_data = NULL;
recent_printed_line = 0;
// prog = NULL;
prog_strings_table = NULL;
prog_debug_strings_table = NULL;
prog_numbers_table = NULL;
prog_opcodes = NULL;
prog_debug_info = NULL;
prog_num_debug_infos = 0;
prog_max_up_count = 0;
}
OS::Core::Compiler::~Compiler()
{
if(recent_printed_text_data){
recent_printed_text_data->release();
}
if(prog_numbers_table){
allocator->core->deleteTable(prog_numbers_table);
prog_numbers_table = NULL;
}
if(prog_strings_table){
allocator->core->deleteTable(prog_strings_table);
prog_strings_table = NULL;
}
if(prog_debug_strings_table){
allocator->core->deleteTable(prog_debug_strings_table);
prog_debug_strings_table = NULL;
}
allocator->vectorClear(prog_numbers);
allocator->vectorClear(prog_strings);
allocator->vectorClear(prog_debug_strings);
allocator->vectorClear(prog_functions);
allocator->deleteObj(prog_opcodes);
allocator->deleteObj(prog_debug_info);
// allocator->deleteObj(tokenizer);
}
bool OS::Core::Compiler::compile()
{
OS_ASSERT(!prog_opcodes && !prog_strings_table && !prog_debug_strings_table && !prog_numbers_table);
OS_ASSERT(!prog_functions.count && !prog_numbers.count && !prog_strings.count);
Scope * scope = NULL;
if(tokenizer->isError()){
setError(ERROR_SYNTAX, NULL);
}else if(!readToken()){
setError(ERROR_EXPECT_TOKEN, recent_token);
}else{
scope = expectTextExpression();
}
if(scope){
Expression * exp = postProcessExpression(scope, scope);
OS_ASSERT(exp->type == EXP_TYPE_FUNCTION);
prog_strings_table = allocator->core->newTable(OS_DBG_FILEPOS_START);
prog_numbers_table = allocator->core->newTable(OS_DBG_FILEPOS_START);
prog_opcodes = new (malloc(sizeof(MemStreamWriter) OS_DBG_FILEPOS)) MemStreamWriter(allocator);
OS::String filename(allocator, tokenizer->getTextData()->filename);
bool is_eval = filename.getDataSize() == 0;
if(!is_eval && allocator->core->settings.create_debug_opcodes){
Core::StringBuffer dump(allocator);
exp->debugPrint(dump, this, 0);
OS::String dump_filename = allocator->getDebugOpcodesFilename(filename);
FileStreamWriter(allocator, dump_filename).writeBytes(dump.buf, dump.count * sizeof(OS_CHAR));
}
prog_debug_strings_table = allocator->core->newTable(OS_DBG_FILEPOS_START);
prog_debug_info = new (malloc(sizeof(MemStreamWriter) OS_DBG_FILEPOS)) MemStreamWriter(allocator);
if(!writeOpcodes(scope, exp)){
// TODO:
}
MemStreamWriter mem_writer(allocator);
MemStreamWriter debuginfo_mem_writer(allocator);
saveToStream(&mem_writer, &debuginfo_mem_writer);
if(!is_eval && allocator->core->settings.create_compiled_file){
OS::String compiled_filename = allocator->getCompiledFilename(filename);
FileStreamWriter(allocator, compiled_filename).writeBytes(mem_writer.buffer.buf, mem_writer.buffer.count);
if(allocator->core->settings.create_debug_info){
OS::String debug_info_filename = allocator->getDebugInfoFilename(filename);
FileStreamWriter(allocator, debug_info_filename).writeBytes(debuginfo_mem_writer.buffer.buf, debuginfo_mem_writer.buffer.count);
}
}
Program * prog = new (malloc(sizeof(Program) OS_DBG_FILEPOS)) Program(allocator);
prog->filename = tokenizer->getTextData()->filename;
MemStreamReader mem_reader(NULL, mem_writer.buffer.buf, mem_writer.buffer.count);
MemStreamReader debuginfo_mem_reader(NULL, debuginfo_mem_writer.buffer.buf, debuginfo_mem_writer.buffer.count);
prog->loadFromStream(&mem_reader, &debuginfo_mem_reader);
prog->pushStartFunction();
prog->release();
allocator->deleteObj(exp);
return true;
}else{
Core::StringBuffer dump(allocator);
dump += OS_TEXT("Error");
switch(error){
default:
dump += OS_TEXT(" unknown");
break;
case ERROR_SYNTAX:
dump += OS_TEXT(" SYNTAX");
break;
case ERROR_NESTED_ROOT_BLOCK:
dump += OS_TEXT(" NESTED_ROOT_BLOCK");
break;
case ERROR_LOCAL_VAL_NOT_DECLARED:
dump += OS_TEXT(" LOCAL_VAL_NOT_DECLARED");
break;
case ERROR_VAR_ALREADY_EXIST:
dump += OS_TEXT(" VAR_ALREADY_EXIST");
break;
case ERROR_VAR_NAME:
dump += OS_TEXT(" VAR_NAME");
break;
case ERROR_EXPECT_TOKEN_TYPE:
dump += OS_TEXT(" EXPECT_TOKEN_TYPE ");
dump += Tokenizer::getTokenTypeName(expect_token_type);
break;
case ERROR_EXPECT_TOKEN_STR:
dump += OS_TEXT(" EXPECT_TOKEN_STR ");
dump += expect_token;
break;
case ERROR_EXPECT_TOKEN:
dump += OS_TEXT(" EXPECT_TOKEN");
break;
case ERROR_EXPECT_VALUE:
dump += OS_TEXT(" EXPECT_VALUE");
break;
case ERROR_EXPECT_WRITEABLE:
dump += OS_TEXT(" EXPECT_WRITEABLE");
break;
case ERROR_EXPECT_GET_OR_SET:
dump += OS_TEXT(" EXPECT_GET_OR_SET");
break;
case ERROR_EXPECT_EXPRESSION:
dump += OS_TEXT(" EXPECT_EXPRESSION");
break;
case ERROR_EXPECT_FUNCTION_SCOPE:
dump += OS_TEXT(" EXPECT_FUNCTION_SCOPE");
break;
case ERROR_EXPECT_CODE_SEP_BEFORE_NESTED_BLOCK:
dump += OS_TEXT(" EXPECT_CODE_SEP_BEFORE_NESTED_BLOCK");
break;
case ERROR_EXPECT_SWITCH_SCOPE:
dump += OS_TEXT(" EXPECT_SWITCH_SCOPE");
break;
case ERROR_FINISH_BINARY_OP:
dump += OS_TEXT(" FINISH_BINARY_OP");
break;
case ERROR_FINISH_UNARY_OP:
dump += OS_TEXT(" FINISH_UNARY_OP");
break;
}
dump += OS_TEXT("\n");
if(error_token){
if(error_token->text_data->filename.getDataSize() > 0){
dump += OS::Core::String::format(allocator, "filename %s\n", error_token->text_data->filename.toChar());
}
dump += OS::Core::String::format(allocator, "[%d] %s\n", error_token->line+1, error_token->text_data->lines[error_token->line].toChar());
dump += OS::Core::String::format(allocator, "pos %d, token: %s\n", error_token->pos+1, error_token->str.toChar());
}else if(tokenizer->isError()){
if(tokenizer->getFilename().getDataSize() > 0){
dump += OS::Core::String::format(allocator, "filename %s\n", tokenizer->getFilename().toChar());
}
dump += OS::Core::String::format(allocator, "[%d] %s\n", tokenizer->getErrorLine()+1, tokenizer->getLineString(tokenizer->getErrorLine()).toChar());
dump += OS::Core::String::format(allocator, "pos %d\n", tokenizer->getErrorPos()+1);
}
allocator->printf("%s", dump.toString().toChar());
// FileStreamWriter(allocator, "test-data/debug-exp-dump.txt").writeBytes(dump.toChar(), dump.getDataSize());
allocator->pushNull();
}
return false;
}
void * OS::Core::Compiler::malloc(int size OS_DBG_FILEPOS_DECL)
{
return allocator->malloc(size OS_DBG_FILEPOS_PARAM);
}
void OS::Core::Compiler::resetError()
{
error = ERROR_NOTHING;
error_token = NULL;
expect_token_type = Tokenizer::NOTHING;
}
void OS::Core::Compiler::setError(ErrorType value, TokenData * error_token)
{
OS_ASSERT(!isError());
error = value;
this->error_token = error_token;
expect_token_type = Tokenizer::NOTHING;
}
void OS::Core::Compiler::setError(TokenType expect_token_type, TokenData * error_token)
{
OS_ASSERT(!isError());
error = ERROR_EXPECT_TOKEN_TYPE;
this->error_token = error_token;
this->expect_token_type = expect_token_type;
}
void OS::Core::Compiler::setError(const String& str, TokenData * error_token)
{
OS_ASSERT(!isError());
error = ERROR_EXPECT_TOKEN_STR;
this->error_token = error_token;
expect_token_type = Tokenizer::NOTHING;
expect_token = str;
}
bool OS::Core::Compiler::isError()
{
return error != ERROR_NOTHING;
}
OS::Core::Compiler::ExpressionType OS::Core::Compiler::getUnaryExpressionType(TokenType token_type)
{
switch(token_type){
case Tokenizer::OPERATOR_LENGTH: return EXP_TYPE_LENGTH;
case Tokenizer::OPERATOR_BIT_NOT: return EXP_TYPE_BIT_NOT;
case Tokenizer::OPERATOR_ADD: return EXP_TYPE_PLUS;
case Tokenizer::OPERATOR_SUB: return EXP_TYPE_NEG;
case Tokenizer::OPERATOR_LOGIC_NOT: return EXP_TYPE_LOGIC_NOT;
}
return EXP_TYPE_UNKNOWN;
}
OS::Core::Compiler::ExpressionType OS::Core::Compiler::getExpressionType(TokenType token_type)
{
switch(token_type){
case Tokenizer::PARAM_SEPARATOR: return EXP_TYPE_PARAMS;
case Tokenizer::OPERATOR_INDIRECT: return EXP_TYPE_INDIRECT;
case Tokenizer::OPERATOR_CONCAT: return EXP_TYPE_CONCAT;
case Tokenizer::OPERATOR_LENGTH: return EXP_TYPE_LENGTH;
case Tokenizer::OPERATOR_LOGIC_AND: return EXP_TYPE_LOGIC_AND;
case Tokenizer::OPERATOR_LOGIC_OR: return EXP_TYPE_LOGIC_OR;
case Tokenizer::OPERATOR_LOGIC_PTR_EQ: return EXP_TYPE_LOGIC_PTR_EQ;
case Tokenizer::OPERATOR_LOGIC_PTR_NE: return EXP_TYPE_LOGIC_PTR_NE;
case Tokenizer::OPERATOR_LOGIC_EQ: return EXP_TYPE_LOGIC_EQ;
case Tokenizer::OPERATOR_LOGIC_NE: return EXP_TYPE_LOGIC_NE;
case Tokenizer::OPERATOR_LOGIC_GE: return EXP_TYPE_LOGIC_GE;
case Tokenizer::OPERATOR_LOGIC_LE: return EXP_TYPE_LOGIC_LE;
case Tokenizer::OPERATOR_LOGIC_GREATER: return EXP_TYPE_LOGIC_GREATER;
case Tokenizer::OPERATOR_LOGIC_LESS: return EXP_TYPE_LOGIC_LESS;
case Tokenizer::OPERATOR_LOGIC_NOT: return EXP_TYPE_LOGIC_NOT;
// case Tokenizer::OPERATOR_INC: return EXP_TYPE_INC;
// case Tokenizer::OPERATOR_DEC: return EXP_TYPE_DEC;
case Tokenizer::OPERATOR_QUESTION: return EXP_TYPE_QUESTION;
// case Tokenizer::OPERATOR_COLON: return ;
case Tokenizer::OPERATOR_IN: return EXP_TYPE_IN;
case Tokenizer::OPERATOR_ISPROTOTYPEOF: return EXP_TYPE_ISPROTOTYPEOF;
case Tokenizer::OPERATOR_IS: return EXP_TYPE_IS;
case Tokenizer::OPERATOR_BIT_AND: return EXP_TYPE_BIT_AND;
case Tokenizer::OPERATOR_BIT_OR: return EXP_TYPE_BIT_OR;
case Tokenizer::OPERATOR_BIT_XOR: return EXP_TYPE_BIT_XOR;
case Tokenizer::OPERATOR_BIT_NOT: return EXP_TYPE_BIT_NOT;
case Tokenizer::OPERATOR_ADD: return EXP_TYPE_ADD;
case Tokenizer::OPERATOR_SUB: return EXP_TYPE_SUB;
case Tokenizer::OPERATOR_MUL: return EXP_TYPE_MUL;
case Tokenizer::OPERATOR_DIV: return EXP_TYPE_DIV;
case Tokenizer::OPERATOR_MOD: return EXP_TYPE_MOD;
case Tokenizer::OPERATOR_LSHIFT: return EXP_TYPE_LSHIFT;
case Tokenizer::OPERATOR_RSHIFT: return EXP_TYPE_RSHIFT;
case Tokenizer::OPERATOR_POW: return EXP_TYPE_POW;
case Tokenizer::OPERATOR_BIT_AND_ASSIGN: return EXP_TYPE_BIT_AND_ASSIGN;
case Tokenizer::OPERATOR_BIT_OR_ASSIGN: return EXP_TYPE_BIT_OR_ASSIGN;
case Tokenizer::OPERATOR_BIT_XOR_ASSIGN: return EXP_TYPE_BIT_XOR_ASSIGN;
case Tokenizer::OPERATOR_BIT_NOT_ASSIGN: return EXP_TYPE_BIT_NOT_ASSIGN;
case Tokenizer::OPERATOR_ADD_ASSIGN: return EXP_TYPE_ADD_ASSIGN;
case Tokenizer::OPERATOR_SUB_ASSIGN: return EXP_TYPE_SUB_ASSIGN;
case Tokenizer::OPERATOR_MUL_ASSIGN: return EXP_TYPE_MUL_ASSIGN;
case Tokenizer::OPERATOR_DIV_ASSIGN: return EXP_TYPE_DIV_ASSIGN;
case Tokenizer::OPERATOR_MOD_ASSIGN: return EXP_TYPE_MOD_ASSIGN;
case Tokenizer::OPERATOR_LSHIFT_ASSIGN: return EXP_TYPE_LSHIFT_ASSIGN;
case Tokenizer::OPERATOR_RSHIFT_ASSIGN: return EXP_TYPE_RSHIFT_ASSIGN;
case Tokenizer::OPERATOR_POW_ASSIGN: return EXP_TYPE_POW_ASSIGN;
case Tokenizer::OPERATOR_ASSIGN: return EXP_TYPE_ASSIGN;
}
return EXP_TYPE_UNKNOWN;
}
OS::Core::Compiler::OpcodeLevel OS::Core::Compiler::getOpcodeLevel(ExpressionType exp_type)
{
switch(exp_type){
case EXP_TYPE_ASSIGN: // =
case EXP_TYPE_BIT_AND_ASSIGN: // &=
case EXP_TYPE_BIT_OR_ASSIGN: // |=
case EXP_TYPE_BIT_XOR_ASSIGN: // ^=
case EXP_TYPE_BIT_NOT_ASSIGN: // ~=
case EXP_TYPE_ADD_ASSIGN: // +=
case EXP_TYPE_SUB_ASSIGN: // -=
case EXP_TYPE_MUL_ASSIGN: // *=
case EXP_TYPE_DIV_ASSIGN: // /=
case EXP_TYPE_MOD_ASSIGN: // %=
case EXP_TYPE_LSHIFT_ASSIGN: // <<=
case EXP_TYPE_RSHIFT_ASSIGN: // >>=
case EXP_TYPE_POW_ASSIGN: // **=
return OP_LEVEL_1;
case EXP_TYPE_PARAMS: // ,
return OP_LEVEL_1_1;
case EXP_TYPE_QUESTION: // ? :
return OP_LEVEL_2;
case EXP_TYPE_LOGIC_OR: // ||
return OP_LEVEL_3;
case EXP_TYPE_LOGIC_AND: // &&
return OP_LEVEL_4;
case EXP_TYPE_CONCAT: // ..
return OP_LEVEL_5;
case EXP_TYPE_LOGIC_PTR_EQ: // ===
case EXP_TYPE_LOGIC_PTR_NE: // !==
case EXP_TYPE_LOGIC_EQ: // ==
case EXP_TYPE_LOGIC_NE: // !=
return OP_LEVEL_6;
case EXP_TYPE_LOGIC_GE: // >=
case EXP_TYPE_LOGIC_LE: // <=
case EXP_TYPE_LOGIC_GREATER: // >
case EXP_TYPE_LOGIC_LESS: // <
return OP_LEVEL_7;
case EXP_TYPE_BIT_OR: // |
return OP_LEVEL_8;
case EXP_TYPE_BIT_AND: // &
case EXP_TYPE_BIT_XOR: // ^
return OP_LEVEL_9;
case EXP_TYPE_LSHIFT: // <<
case EXP_TYPE_RSHIFT: // >>
return OP_LEVEL_10;
case EXP_TYPE_ADD: // +
case EXP_TYPE_SUB: // -
return OP_LEVEL_11;
case EXP_TYPE_MUL: // *
case EXP_TYPE_DIV: // /
case EXP_TYPE_MOD: // %
return OP_LEVEL_12;
case EXP_TYPE_POW: // **
case EXP_TYPE_IN:
case EXP_TYPE_ISPROTOTYPEOF:
case EXP_TYPE_IS:
return OP_LEVEL_13;
case EXP_TYPE_PRE_INC: // ++
case EXP_TYPE_PRE_DEC: // --
case EXP_TYPE_POST_INC: // ++
case EXP_TYPE_POST_DEC: // --
return OP_LEVEL_14;
case EXP_TYPE_TYPE_OF:
case EXP_TYPE_VALUE_OF:
case EXP_TYPE_NUMBER_OF:
case EXP_TYPE_STRING_OF:
case EXP_TYPE_ARRAY_OF:
case EXP_TYPE_OBJECT_OF:
case EXP_TYPE_USERDATA_OF:
case EXP_TYPE_FUNCTION_OF:
case EXP_TYPE_LOGIC_BOOL: // !!
case EXP_TYPE_LOGIC_NOT: // !
case EXP_TYPE_PLUS: // +
case EXP_TYPE_NEG: // -
case EXP_TYPE_LENGTH: // #
case EXP_TYPE_BIT_NOT: // ~
return OP_LEVEL_15;
case EXP_TYPE_INDIRECT:
return OP_LEVEL_16;
}
return OP_LEVEL_0;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::getPrevToken()
{
int i = next_token_index-2;
return i >= 0 ? tokenizer->getToken(i) : NULL;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::readToken()
{
if(isError()){
return NULL;
}
if(next_token_index < tokenizer->getNumTokens()){
return recent_token = tokenizer->getToken(next_token_index++);
}
return recent_token = NULL;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::setNextTokenIndex(int i)
{
OS_ASSERT(tokenizer && i >= 0 && i <= tokenizer->getNumTokens());
next_token_index = i;
return recent_token = next_token_index > 0 ? tokenizer->getToken(next_token_index-1) : NULL;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::setNextToken(TokenData * token)
{
OS_ASSERT(tokenizer);
int i, count = tokenizer->getNumTokens();
for(i = next_token_index - 1; i >= 0; i--){
if(tokenizer->getToken(i) == token)
break;
}
if(i < 0){
for(i = next_token_index; i < count; i++){
if(tokenizer->getToken(i) == token)
break;
}
}
if(i >= 0 && i < count){
next_token_index = i;
return recent_token = next_token_index > 0 ? tokenizer->getToken(next_token_index-1) : NULL;
}
OS_ASSERT(false);
return NULL;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::putNextTokenType(TokenType token_type)
{
if(token_type == Tokenizer::CODE_SEPARATOR && recent_token && recent_token->type == token_type){
return ungetToken();
}
TokenData * token = recent_token;
if(readToken()){
if(recent_token->type == token_type){
return ungetToken();
}
ungetToken();
token = recent_token;
}
if(!token){
if(next_token_index > 0){
token = tokenizer->getToken(next_token_index-1);
}
}
if(token){
token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(token->text_data, String(allocator), token_type, token->line, token->pos);
}else{
token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), String(allocator), token_type, 0, 0);
}
tokenizer->insertToken(next_token_index, token OS_DBG_FILEPOS);
return token;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::ungetToken()
{
return setNextTokenIndex(next_token_index-1);
}
bool OS::Core::Compiler::isNextTokens(TokenType * list, int count)
{
bool ok = true;
int save_next_token_index = next_token_index;
for(int i = 0; i < count; i++){
if(!readToken() || !recent_token->isTypeOf(list[i])){
ok = false;
break;
}
}
setNextTokenIndex(save_next_token_index);
return ok;
}
bool OS::Core::Compiler::isNextToken(TokenType t0)
{
return isNextTokens(&t0, 1);
}
bool OS::Core::Compiler::isNextTokens(TokenType t0, TokenType t1)
{
TokenType list[] = {t0, t1};
return isNextTokens(list, sizeof(list)/sizeof(list[0]));
}
bool OS::Core::Compiler::isNextTokens(TokenType t0, TokenType t1, TokenType t2)
{
TokenType list[] = {t0, t1, t2};
return isNextTokens(list, sizeof(list)/sizeof(list[0]));
}
bool OS::Core::Compiler::isNextTokens(TokenType t0, TokenType t1, TokenType t2, TokenType t3)
{
TokenType list[] = {t0, t1, t2, t3};
return isNextTokens(list, sizeof(list)/sizeof(list[0]));
}
void OS::Core::Compiler::deleteNops(ExpressionList& list)
{
for(int i = 0; i < list.count; i++){
Expression * exp = list[i];
switch(exp->type){
case EXP_TYPE_NOP:
allocator->deleteObj(exp);
list.removeIndex(i--);
break;
}
}
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::expectToken(TokenType type)
{
if(isError()){
return NULL;
}
if(!readToken() || recent_token->type != type){
setError(type, recent_token);
return NULL;
}
return recent_token;
}
OS::Core::Tokenizer::TokenData * OS::Core::Compiler::expectToken()
{
if(isError()){
return NULL;
}
if(!readToken()){
setError(ERROR_EXPECT_TOKEN, recent_token);
return NULL;
}
return recent_token;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectExpressionValues(Expression * exp, int ret_values)
{
if(exp->ret_values == ret_values || ret_values < 0){
return exp;
}
switch(exp->type){
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
case EXP_TYPE_CALL_DIM:
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
case EXP_TYPE_GET_PROPERTY:
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
// case EXP_TYPE_GET_PROPERTY_DIM:
case EXP_TYPE_INDIRECT:
// case EXP_TYPE_GET_ENV_VAR_DIM:
case EXP_TYPE_TAIL_CALL: // ret values are not used for tail call
case EXP_TYPE_TAIL_CALL_METHOD: // ret values are not used for tail call
exp->ret_values = ret_values;
return exp;
case EXP_TYPE_CODE_LIST:
if(exp->list.count > 0){
Expression * last_exp = exp->list[exp->list.count-1];
switch(last_exp->type){
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
case EXP_TYPE_CALL_DIM:
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
case EXP_TYPE_GET_PROPERTY:
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
// case EXP_TYPE_GET_PROPERTY_DIM:
case EXP_TYPE_INDIRECT:
// case EXP_TYPE_GET_ENV_VAR_DIM:
case EXP_TYPE_TAIL_CALL: // ret values are not used for tail call
case EXP_TYPE_TAIL_CALL_METHOD: // ret values are not used for tail call
last_exp->ret_values = ret_values;
exp->ret_values = ret_values;
return exp;
case EXP_TYPE_RETURN:
last_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, last_exp->token, last_exp OS_DBG_FILEPOS);
exp->list[exp->list.count-1] = last_exp;
last_exp->ret_values = ret_values;
exp->ret_values = ret_values;
return exp;
}
}
break;
case EXP_TYPE_RETURN:
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = ret_values;
return exp;
case EXP_TYPE_PARAMS:
if(exp->ret_values > ret_values){
for(int i = exp->list.count-1; exp->ret_values > ret_values && i >= 0; i--){
Expression * param_exp = exp->list[i];
if(param_exp->type == EXP_TYPE_PARAMS){
break;
}
OS_ASSERT(param_exp->type != EXP_TYPE_PARAMS);
OS_ASSERT(param_exp->type != EXP_TYPE_RETURN);
OS_ASSERT(param_exp->type != EXP_TYPE_CODE_LIST);
if(param_exp->isConstValue()){
exp->list.removeIndex(i);
exp->ret_values--;
allocator->deleteObj(param_exp);
continue;
}
switch(param_exp->type){
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
case EXP_TYPE_CALL_DIM:
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
case EXP_TYPE_GET_PROPERTY:
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
// case EXP_TYPE_GET_PROPERTY_DIM:
// case EXP_TYPE_GET_ENV_VAR_DIM:
case EXP_TYPE_INDIRECT:
if(exp->ret_values <= param_exp->ret_values){
param_exp->ret_values -= exp->ret_values;
exp->ret_values = 0;
}else{
exp->ret_values -= param_exp->ret_values;
param_exp->ret_values = 0;
}
continue;
}
break;
}
}
break;
case EXP_TYPE_PRE_INC:
case EXP_TYPE_PRE_DEC:
case EXP_TYPE_POST_INC:
case EXP_TYPE_POST_DEC:
OS_ASSERT(exp->ret_values == 1);
if(!ret_values){
exp->ret_values = 0;
return exp;
}
break;
}
while(exp->ret_values > ret_values){
int new_ret_values = exp->ret_values-1;
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_POP_VALUE, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = new_ret_values;
}
if(exp->ret_values < ret_values){
if(exp->type != EXP_TYPE_PARAMS){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = exp->list[0]->ret_values;
}
while(exp->ret_values < ret_values){
Expression * null_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NULL, exp->token);
null_exp->ret_values = 1;
exp->list.add(null_exp OS_DBG_FILEPOS);
exp->ret_values++;
}
}
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::newSingleValueExpression(Expression * exp)
{
exp = expectExpressionValues(exp, 1);
switch(exp->type){
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
case EXP_TYPE_CALL_DIM:
// case EXP_TYPE_GET_DIM:
case EXP_TYPE_CALL_METHOD:
case EXP_TYPE_GET_PROPERTY:
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
// case EXP_TYPE_GET_PROPERTY_DIM:
// case EXP_TYPE_GET_ENV_VAR_DIM:
case EXP_TYPE_INDIRECT:
{
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_VALUE, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
break;
}
}
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::newExpressionFromList(ExpressionList& list, int ret_values)
{
Expression * exp;
if(list.count == 1){
exp = list[0];
list.removeIndex(0);
}else if(list.count == 0){
TokenData * cur_token = ungetToken();
readToken();
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, cur_token);
}else{
int i;
for(i = 0; i < list.count-1; i++){
OS_ASSERT(list[i]->type != EXP_TYPE_CODE_LIST);
list[i] = expectExpressionValues(list[i], 0);
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, list[0]->token);
exp->list.swap(list);
exp->ret_values = exp->list[exp->list.count-1]->ret_values;
}
return expectExpressionValues(exp, ret_values);
}
OS::Core::Compiler::Expression * OS::Core::Compiler::stepPass2(Scope * scope, Expression * exp)
{
switch(exp->type){
case EXP_TYPE_FUNCTION:
{
Scope * new_scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(new_scope && (new_scope->parent == scope || (!new_scope->parent && new_scope->type == EXP_TYPE_FUNCTION)));
if(new_scope != scope){
new_scope->func_index = scope->function->num_local_funcs++;
new_scope->func_depth = scope->function->func_depth + 1;
}
scope = new_scope;
OS_ASSERT(prog_functions.indexOf(scope) < 0);
scope->prog_func_index = prog_functions.count;
allocator->vectorAddItem(prog_functions, scope OS_DBG_FILEPOS);
break;
}
case EXP_TYPE_SCOPE:
case EXP_TYPE_LOOP_SCOPE:
{
Scope * new_scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(new_scope && (new_scope->parent == scope || (!new_scope->parent && new_scope->type == EXP_TYPE_FUNCTION)));
scope = new_scope;
break;
}
case EXP_TYPE_DEBUG_LOCALS:
if(exp->list.count == 0){
Expression * obj_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT, exp->token);
Vector<String> vars;
// skip globals & env vars
allocator->vectorAddItem(vars, allocator->core->strings->var_env OS_DBG_FILEPOS);
#ifdef OS_GLOBAL_VAR_ENABLED
allocator->vectorAddItem(vars, allocator->core->strings->var_globals OS_DBG_FILEPOS);
#endif
Scope * start_scope = scope;
for(; scope; scope = scope->parent){
for(int i = scope->locals.count-1; i >= 0; i--){
const Scope::LocalVar& local_var = scope->locals[i];
if(local_var.name.toChar()[0] == OS_TEXT('#')){
continue;
}
bool found = false;
for(int j = 0; j < vars.count; j++){
if(vars[j] == local_var.name){
found = true;
break;
}
}
if(found){
continue;
}
allocator->vectorAddItem(vars, local_var.name OS_DBG_FILEPOS);
TokenData * name_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), local_var.name,
Tokenizer::NAME, exp->token->line, exp->token->pos);
Expression * var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, name_token);
OS_ASSERT(scope->function);
var_exp->active_locals = scope->function->num_locals;
var_exp->ret_values = 1;
found = findLocalVar(var_exp->local_var, start_scope, local_var.name, start_scope->function->num_locals, true);
OS_ASSERT(found); // && var_exp->local_var.index == local_var.index);
if(start_scope->function->max_up_count < var_exp->local_var.up_count){
start_scope->function->max_up_count = var_exp->local_var.up_count;
}
Expression * obj_item_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT_SET_BY_NAME, name_token, var_exp OS_DBG_FILEPOS);
allocator->vectorInsertAtIndex(obj_exp->list, 0, obj_item_exp OS_DBG_FILEPOS);
name_token->release();
}
}
allocator->vectorClear(vars);
obj_exp->ret_values = 1;
scope = start_scope;
exp->list.add(obj_exp OS_DBG_FILEPOS);
}
break;
case EXP_TYPE_PARAMS:
{
for(int i = exp->list.count-1; i >= 0; i--){
Expression * sub_exp = stepPass2(scope, exp->list[i]);
if(sub_exp->type == EXP_TYPE_PARAMS){
// OS_ASSERT(false);
ExpressionList list(allocator);
int j;
for(j = 0; j < i; j++){
list.add(exp->list[j] OS_DBG_FILEPOS);
}
for(j = 0; j < sub_exp->list.count; j++){
list.add(sub_exp->list[j] OS_DBG_FILEPOS);
}
for(j = i+1; j < exp->list.count; j++){
list.add(exp->list[j] OS_DBG_FILEPOS);
}
exp->ret_values += sub_exp->ret_values;
list.swap(exp->list);
allocator->vectorClear(list);
allocator->vectorClear(sub_exp->list);
allocator->deleteObj(sub_exp);
}else{
exp->list[i] = sub_exp;
}
}
return exp;
}
case EXP_TYPE_POST_INC:
case EXP_TYPE_POST_DEC:
OS_ASSERT(exp->list.count == 1);
if(exp->ret_values > 0){
OS_ASSERT(exp->ret_values == 1);
exp->list[0] = stepPass2(scope, exp->list[0]);
Expression * var_exp = exp->list[0];
OS_ASSERT(var_exp->type == EXP_TYPE_GET_LOCAL_VAR);
String temp_var_name = String(allocator, OS_TEXT("#temp")); // + String(allocator, (OS_INT)scope->function->num_locals+1);
TokenData * temp_var_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), temp_var_name, Tokenizer::NAME, exp->token->line, exp->token->pos);
TokenData * num_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), String(allocator, OS_TEXT("1")), Tokenizer::NUMBER, exp->token->line, exp->token->pos);
num_token->setFloat(1);
Expression * cur_var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, var_exp->token);
cur_var_exp->ret_values = 1;
cur_var_exp->local_var = var_exp->local_var;
Expression * result_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, exp->token);
Expression * copy_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_SET_LOCAL_VAR, temp_var_token, cur_var_exp OS_DBG_FILEPOS);
OS_ASSERT(!findLocalVar(copy_exp->local_var, scope, temp_var_name, scope->function->num_locals, false));
scope->addLocalVar(temp_var_name, copy_exp->local_var);
result_exp->list.add(copy_exp OS_DBG_FILEPOS);
cur_var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, var_exp->token);
cur_var_exp->ret_values = 1;
cur_var_exp->local_var = var_exp->local_var;
Expression * num_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NUMBER, num_token);
num_exp->ret_values = 1;
Expression * op_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp->type == EXP_TYPE_POST_INC ? EXP_TYPE_ADD : EXP_TYPE_SUB, exp->token, cur_var_exp, num_exp OS_DBG_FILEPOS);
op_exp->ret_values = 1;
Expression * set_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_SET_LOCAL_VAR, var_exp->token, op_exp OS_DBG_FILEPOS);
set_exp->local_var = var_exp->local_var;
result_exp->list.add(set_exp OS_DBG_FILEPOS);
Expression * get_temp_var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, temp_var_token);
get_temp_var_exp->ret_values = 1;
get_temp_var_exp->local_var = copy_exp->local_var;
result_exp->list.add(get_temp_var_exp OS_DBG_FILEPOS);
result_exp->ret_values = 1;
temp_var_token->release();
num_token->release();
allocator->deleteObj(exp);
return stepPass2(scope, result_exp);
}
exp->type = exp->type == EXP_TYPE_POST_INC ? EXP_TYPE_PRE_INC : EXP_TYPE_PRE_DEC;
// no break
case EXP_TYPE_PRE_INC:
case EXP_TYPE_PRE_DEC:
{
OS_ASSERT(exp->list.count == 1);
exp->list[0] = stepPass2(scope, exp->list[0]);
Expression * var_exp = exp->list[0];
OS_ASSERT(var_exp->type == EXP_TYPE_GET_LOCAL_VAR);
TokenData * num_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), String(allocator, OS_TEXT("1")), Tokenizer::NUMBER, exp->token->line, exp->token->pos);
num_token->setFloat(1);
Expression * cur_var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, var_exp->token);
cur_var_exp->ret_values = 1;
cur_var_exp->local_var = var_exp->local_var;
Expression * num_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NUMBER, num_token);
num_exp->ret_values = 1;
Expression * op_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp->type == EXP_TYPE_PRE_INC ? EXP_TYPE_ADD : EXP_TYPE_SUB, exp->token, cur_var_exp, num_exp OS_DBG_FILEPOS);
op_exp->ret_values = 1;
Expression * set_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_SET_LOCAL_VAR, var_exp->token, op_exp OS_DBG_FILEPOS);
set_exp->local_var = var_exp->local_var;
Expression * result_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CODE_LIST, exp->token);
result_exp->list.add(set_exp OS_DBG_FILEPOS);
if(exp->ret_values > 0){
OS_ASSERT(exp->ret_values == 1);
cur_var_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, var_exp->token);
cur_var_exp->ret_values = 1;
cur_var_exp->local_var = var_exp->local_var;
result_exp->list.add(cur_var_exp OS_DBG_FILEPOS);
result_exp->ret_values = 1;
}
allocator->deleteObj(exp);
num_token->release();
return stepPass2(scope, result_exp);
}
case EXP_TYPE_NAME:
if(findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, true)){
exp->type = EXP_TYPE_GET_LOCAL_VAR;
if(scope->function->max_up_count < exp->local_var.up_count){
scope->function->max_up_count = exp->local_var.up_count;
}
}else{
exp->type = EXP_TYPE_GET_ENV_VAR;
}
break;
case EXP_TYPE_RETURN:
if(exp->list.count == 1){
Expression * sub_exp = exp->list[0] = stepPass2(scope, exp->list[0]);
switch(sub_exp->type){
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
sub_exp->type = EXP_TYPE_TAIL_CALL;
allocator->vectorClear(exp->list);
allocator->deleteObj(exp);
return sub_exp;
case EXP_TYPE_CALL_METHOD:
sub_exp->type = EXP_TYPE_TAIL_CALL_METHOD;
allocator->vectorClear(exp->list);
allocator->deleteObj(exp);
return sub_exp;
}
return exp;
}
break;
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
{
OS_ASSERT(exp->list.count == 2);
exp->list[0] = stepPass2(scope, exp->list[0]);
exp->list[1] = stepPass2(scope, exp->list[1]);
Expression * left_exp = exp->list[0];
Expression * right_exp = exp->list[1];
if(left_exp->type == EXP_TYPE_GET_PROPERTY){
OS_ASSERT(left_exp->list.count == 2);
OS_ASSERT(right_exp->type == EXP_TYPE_PARAMS);
allocator->vectorInsertAtIndex(right_exp->list, 0, left_exp->list[1] OS_DBG_FILEPOS);
right_exp->ret_values += left_exp->list[1]->ret_values;
left_exp->list[1] = right_exp;
left_exp->type = EXP_TYPE_CALL_METHOD;
left_exp->ret_values = exp->ret_values;
allocator->vectorClear(exp->list);
allocator->deleteObj(exp);
return left_exp;
}
if(left_exp->type == EXP_TYPE_GET_ENV_VAR){
OS_ASSERT(left_exp->list.count == 0);
OS_ASSERT(right_exp->type == EXP_TYPE_PARAMS);
left_exp->type = EXP_TYPE_CONST_STRING;
allocator->vectorInsertAtIndex(right_exp->list, 0, left_exp OS_DBG_FILEPOS);
right_exp->ret_values++;
TokenData * name_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(),
allocator->core->strings->var_env,
Tokenizer::NAME, left_exp->token->line, left_exp->token->pos);
left_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, name_token);
left_exp->ret_values = 1;
if(!findLocalVar(left_exp->local_var, scope, name_token->str, scope->function->num_params+ENV_VAR_INDEX+1, true)){
OS_ASSERT(false);
};
if(scope->function->max_up_count < left_exp->local_var.up_count){
scope->function->max_up_count = left_exp->local_var.up_count;
}
exp->list[0] = left_exp;
name_token->release();
exp->type = EXP_TYPE_CALL_METHOD;
return exp;
}
/* if(left_exp->type == EXP_TYPE_GET_DIM){
OS_ASSERT(left_exp->list.count == 2);
OS_ASSERT(right_exp->type == EXP_TYPE_PARAMS);
if(left_exp->list[1]->list.count == 1){
Expression * params = left_exp->list[1];
OS_ASSERT(params->type == EXP_TYPE_PARAMS);
allocator->vectorInsertAtIndex(right_exp->list, 0, params->list[0]);
right_exp->ret_values += params->ret_values;
left_exp->list[1] = right_exp;
left_exp->type = EXP_TYPE_CALL_METHOD;
left_exp->ret_values = exp->ret_values;
allocator->vectorClear(params->list);
allocator->deleteObj(params);
allocator->vectorClear(exp->list);
allocator->deleteObj(exp);
return left_exp;
}
} */
return exp;
}
case EXP_TYPE_SET_DIM:
{
OS_ASSERT(exp->list.count == 3);
exp->list[0] = stepPass2(scope, exp->list[0]);
exp->list[1] = stepPass2(scope, exp->list[1]);
exp->list[2] = stepPass2(scope, exp->list[2]);
Expression * params = exp->list[2];
if(params->list.count == 1){
exp->list[2] = params->list[0];
allocator->vectorClear(params->list);
allocator->deleteObj(params);
exp->type = EXP_TYPE_SET_PROPERTY;
for(Expression * get_exp = exp->list[1];;){
switch(get_exp->type){
case EXP_TYPE_GET_PROPERTY:
OS_ASSERT(get_exp->list.count == 2);
get_exp->type = EXP_TYPE_GET_PROPERTY_AUTO_CREATE;
get_exp = get_exp->list[0];
continue;
case EXP_TYPE_GET_LOCAL_VAR:
get_exp->type = EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE;
break;
case EXP_TYPE_GET_ENV_VAR:
get_exp->type = EXP_TYPE_GET_ENV_VAR_AUTO_CREATE;
break;
}
break;
}
return exp;
}
if(params->list.count == 0){
// nop
}
break;
}
case EXP_TYPE_SET_PROPERTY:
{
OS_ASSERT(exp->list.count == 3);
exp->list[0] = stepPass2(scope, exp->list[0]);
exp->list[1] = stepPass2(scope, exp->list[1]);
exp->list[2] = stepPass2(scope, exp->list[2]);
for(Expression * get_exp = exp->list[1];;){
switch(get_exp->type){
case EXP_TYPE_GET_PROPERTY:
OS_ASSERT(get_exp->list.count == 2);
get_exp->type = EXP_TYPE_GET_PROPERTY_AUTO_CREATE;
get_exp = get_exp->list[0];
continue;
case EXP_TYPE_GET_LOCAL_VAR:
get_exp->type = EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE;
break;
case EXP_TYPE_GET_ENV_VAR:
get_exp->type = EXP_TYPE_GET_ENV_VAR_AUTO_CREATE;
break;
}
break;
}
return exp;
}
case EXP_TYPE_CALL_DIM:
{
OS_ASSERT(exp->list.count == 2);
exp->list[0] = stepPass2(scope, exp->list[0]);
exp->list[1] = stepPass2(scope, exp->list[1]);
Expression * name_exp = exp->list[0];
Expression * params = exp->list[1];
OS_ASSERT(params->type == EXP_TYPE_PARAMS);
if(params->list.count == 1){
exp->list[1] = params->list[0];
allocator->vectorClear(params->list);
allocator->deleteObj(params);
exp->type = EXP_TYPE_GET_PROPERTY;
}else{
// exp->type = EXP_TYPE_GET_DIM;
String method_name = !params->list.count ? allocator->core->strings->__getempty : allocator->core->strings->__getdim;
TokenData * token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), method_name, Tokenizer::NAME, name_exp->token->line, name_exp->token->pos);
Expression * exp_method_name = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_STRING, token);
exp_method_name->ret_values = 1;
token->release();
allocator->vectorInsertAtIndex(params->list, 0, exp_method_name OS_DBG_FILEPOS);
params->ret_values++;
exp->type = EXP_TYPE_CALL_METHOD;
}
return exp;
}
case EXP_TYPE_INDIRECT:
{
OS_ASSERT(exp->list.count == 2);
exp->list[0] = expectExpressionValues(exp->list[0], 1);
exp->list[1] = expectExpressionValues(exp->list[1], 1);
Expression * left_exp = exp->list[0];
Expression * right_exp = exp->list[1];
ExpressionType exp_type = EXP_TYPE_GET_PROPERTY;
switch(right_exp->type){
case EXP_TYPE_NAME:
right_exp->type = EXP_TYPE_CONST_STRING;
break;
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
right_exp->type = EXP_TYPE_PARAMS;
exp_type = EXP_TYPE_CALL_METHOD;
OS_ASSERT(right_exp->list.count == 2);
if(right_exp->list[0]->type == EXP_TYPE_NAME){
OS_ASSERT(right_exp->list[0]->ret_values == 1);
right_exp->list[0]->type = EXP_TYPE_CONST_STRING;
}
break;
}
exp->type = exp_type;
break;
}
}
for(int i = 0; i < exp->list.count; i++){
exp->list[i] = stepPass2(scope, exp->list[i]);
}
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::postProcessExpression(Scope * scope, Expression * exp)
{
exp = stepPass2(scope, exp);
#if 0
return exp;
#else
OS_ASSERT(scope->type == EXP_TYPE_FUNCTION);
// prog_stack_size = 0;
return stepPass3(scope, exp);
#endif
}
#if 1 // speed optimization
OS::Core::Compiler::Expression * OS::Core::Compiler::stepPass3(Scope * scope, Expression * exp)
{
struct Lib {
static Expression * processExpList(Compiler * compiler, Scope * scope, Expression * exp)
{
for(int i = 0; i < exp->list.count; i++){
exp->list[i] = compiler->stepPass3(scope, exp->list[i]);
}
return exp;
}
};
Expression * exp1, * exp2;
switch(exp->type){
case EXP_TYPE_FUNCTION:
{
Scope * new_scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(new_scope && (new_scope->parent == scope || (!new_scope->parent && new_scope->type == EXP_TYPE_FUNCTION)));
scope = new_scope;
break;
}
case EXP_TYPE_SCOPE:
case EXP_TYPE_LOOP_SCOPE:
{
Scope * new_scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(new_scope && (new_scope->parent == scope || (!new_scope->parent && new_scope->type == EXP_TYPE_FUNCTION)));
scope = new_scope;
break;
}
case EXP_TYPE_GET_PROPERTY:
{
OS_ASSERT(exp->list.count == 2);
exp = Lib::processExpList(this, scope, exp);
exp1 = exp->list[0];
exp2 = exp->list[1];
if(exp1->type == EXP_TYPE_GET_LOCAL_VAR && exp2->type == EXP_TYPE_GET_LOCAL_VAR
&& !exp1->local_var.up_count && exp1->local_var.index <= 255
&& !exp2->local_var.up_count && exp2->local_var.index <= 255)
{
exp->type = EXP_TYPE_GET_PROPERTY_BY_LOCALS;
}else if(exp1->type == EXP_TYPE_GET_LOCAL_VAR && exp2->type == EXP_TYPE_CONST_NUMBER
&& !exp1->local_var.up_count && exp1->local_var.index <= 255)
{
exp->type = EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER;
}else if(exp1->type == EXP_TYPE_GET_THIS && exp2->type == EXP_TYPE_CONST_STRING){
exp->type = EXP_TYPE_GET_THIS_PROPERTY_BY_STRING;
}
return exp;
}
case EXP_TYPE_SET_PROPERTY:
{
OS_ASSERT(exp->list.count == 3);
exp = Lib::processExpList(this, scope, exp);
exp1 = exp->list[1];
exp2 = exp->list[2];
if(exp1->type == EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE && exp2->type == EXP_TYPE_GET_LOCAL_VAR
&& !exp1->local_var.up_count && exp1->local_var.index <= 255
&& !exp2->local_var.up_count && exp2->local_var.index <= 255)
{
if(exp->list[0]->type == EXP_TYPE_GET_PROPERTY_BY_LOCALS){
exp->type = EXP_TYPE_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE;
}else{
exp->type = EXP_TYPE_SET_PROPERTY_BY_LOCALS_AUTO_CREATE;
}
}
return exp;
}
case EXP_TYPE_CONCAT:
case EXP_TYPE_LOGIC_PTR_EQ:
case EXP_TYPE_LOGIC_PTR_NE:
case EXP_TYPE_LOGIC_EQ:
case EXP_TYPE_LOGIC_NE:
case EXP_TYPE_LOGIC_GE:
case EXP_TYPE_LOGIC_LE:
case EXP_TYPE_LOGIC_GREATER:
case EXP_TYPE_LOGIC_LESS:
case EXP_TYPE_BIT_AND:
case EXP_TYPE_BIT_OR:
case EXP_TYPE_BIT_XOR:
case EXP_TYPE_ADD: // +
case EXP_TYPE_SUB: // -
case EXP_TYPE_MUL: // *
case EXP_TYPE_DIV: // /
case EXP_TYPE_MOD: // %
case EXP_TYPE_LSHIFT: // <<
case EXP_TYPE_RSHIFT: // >>
case EXP_TYPE_POW: // **
{
OS_ASSERT(exp->list.count == 2);
exp = Lib::processExpList(this, scope, exp);
exp1 = exp->list[0];
exp2 = exp->list[1];
if(exp1->type == EXP_TYPE_GET_LOCAL_VAR && exp2->type == EXP_TYPE_GET_LOCAL_VAR
&& !exp1->local_var.up_count && exp1->local_var.index <= 255
&& !exp2->local_var.up_count && exp2->local_var.index <= 255)
{
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_BIN_OPERATOR_BY_LOCALS, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = exp->list[0]->ret_values;
}else if(exp1->type == EXP_TYPE_GET_LOCAL_VAR && exp2->type == EXP_TYPE_CONST_NUMBER
&& !exp1->local_var.up_count && exp1->local_var.index <= 255)
{
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = exp->list[0]->ret_values;
}
return exp;
}
case EXP_TYPE_SET_LOCAL_VAR:
{
OS_ASSERT(exp->list.count == 1);
exp = Lib::processExpList(this, scope, exp);
if(!exp->local_var.up_count && exp->local_var.index <= 255){
exp1 = exp->list[0];
if(exp1->type == EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER){
exp->type = EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER;
}else if(exp1->type == EXP_TYPE_BIN_OPERATOR_BY_LOCALS){
exp->type = EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS;
}
}
return exp;
}
case EXP_TYPE_POST_INC:
case EXP_TYPE_POST_DEC:
case EXP_TYPE_PRE_INC:
case EXP_TYPE_PRE_DEC:
OS_ASSERT(false);
break;
case EXP_TYPE_NAME:
OS_ASSERT(false);
break;
case EXP_TYPE_CALL_DIM:
OS_ASSERT(false);
break;
case EXP_TYPE_INDIRECT:
OS_ASSERT(false);
break;
}
return Lib::processExpList(this, scope, exp);
}
#endif
OS::Core::Compiler::Scope * OS::Core::Compiler::expectTextExpression()
{
OS_ASSERT(recent_token);
Scope * scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(NULL, EXP_TYPE_FUNCTION, recent_token);
// scope->function = scope;
scope->parser_started = true;
scope->ret_values = 1;
scope->addStdVars();
Params p = Params()
.setAllowAssign(true)
.setAllowAutoCall(true)
.setAllowBinaryOperator(true)
.setAllowParams(true)
.setAllowRootBlocks(true);
Expression * exp;
ExpressionList list(allocator);
while(!isError()){
exp = expectSingleExpression(scope, p);
if(isError()){
break;
}
if(exp){
list.add(exp OS_DBG_FILEPOS);
}
if(!recent_token){
break;
}
TokenType token_type = recent_token->type;
if(token_type == Tokenizer::CODE_SEPARATOR){
if(!readToken()){
break;
}
token_type = recent_token->type;
}
if(token_type == Tokenizer::END_ARRAY_BLOCK
|| token_type == Tokenizer::END_BRACKET_BLOCK
|| token_type == Tokenizer::END_CODE_BLOCK)
{
break;
}
}
if(isError()){
allocator->deleteObj(scope);
return NULL;
}
if(recent_token){
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(scope);
return NULL;
}
if(list.count == 0){
return scope;
}
int ret_values = list.count == 1 && list[0]->ret_values > 0 && list[0]->type == EXP_TYPE_FUNCTION ? 1 : 0;
{
putNextTokenType(Tokenizer::CODE_SEPARATOR);
readToken();
TokenData * name_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(),
allocator->core->strings->var_env,
Tokenizer::NAME, recent_token->line, recent_token->pos);
ExpressionList& func_exp_list = ret_values == 1 ? list[0]->list : list;
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, name_token);
name_exp->ret_values = 1;
if(!findLocalVar(name_exp->local_var, scope, allocator->core->strings->var_env, scope->num_locals, false)){
OS_ASSERT(false);
}
OS_ASSERT(name_exp->local_var.up_count == 0);
Expression * ret_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_RETURN, recent_token, name_exp OS_DBG_FILEPOS);
ret_exp->ret_values = 1;
func_exp_list.add(ret_exp OS_DBG_FILEPOS);
name_token->release();
}
exp = newExpressionFromList(list, ret_values);
switch(exp->type){
case EXP_TYPE_CODE_LIST:
if(exp->list.count == 1 && exp->list[0]->type == EXP_TYPE_FUNCTION){
allocator->deleteObj(scope);
scope = dynamic_cast<Scope*>(exp->list[0]);
allocator->vectorClear(exp->list);
allocator->deleteObj(exp);
return scope;
}
// exp = expectExpressionValues(exp, 0);
scope->list.swap(exp->list);
allocator->deleteObj(exp);
break;
case EXP_TYPE_FUNCTION:
OS_ASSERT(scope->num_locals == 0);
allocator->deleteObj(scope);
scope = dynamic_cast<Scope*>(exp);
OS_ASSERT(scope);
scope->parent = NULL;
return scope;
default:
scope->list.add(exp OS_DBG_FILEPOS);
}
return scope;
}
OS::Core::Compiler::Scope * OS::Core::Compiler::expectCodeExpression(Scope * parent, int ret_values)
{
OS_ASSERT(recent_token && recent_token->type == Tokenizer::BEGIN_CODE_BLOCK);
if(!expectToken()){
allocator->deleteObj(parent);
return NULL;
}
Scope * scope;
// bool is_new_func;
if(parent->type == EXP_TYPE_FUNCTION && !parent->parser_started){
scope = parent;
// is_new_func = true;
parent->parser_started = true;
}else{
scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(parent, EXP_TYPE_SCOPE, recent_token);
// scope->function = parent->function;
// is_new_func = false;
}
Params p = Params()
.setAllowAssign(true)
.setAllowAutoCall(true)
.setAllowBinaryOperator(true)
.setAllowParams(true)
.setAllowRootBlocks(true);
Expression * exp;
ExpressionList list(allocator);
while(!isError()){
exp = expectSingleExpression(scope, p);
if(isError()){
break;
}
if(exp){
list.add(exp OS_DBG_FILEPOS);
}
TokenType token_type = recent_token->type;
if(token_type == Tokenizer::CODE_SEPARATOR){
if(!readToken()){
break;
}
token_type = recent_token->type;
}
if(token_type == Tokenizer::END_ARRAY_BLOCK
|| token_type == Tokenizer::END_BRACKET_BLOCK
|| token_type == Tokenizer::END_CODE_BLOCK)
{
break;
}
}
if(isError()){
allocator->deleteObj(scope);
return NULL;
}
if(!recent_token || recent_token->type != Tokenizer::END_CODE_BLOCK){
setError(Tokenizer::END_CODE_BLOCK, recent_token);
allocator->deleteObj(scope);
return NULL;
}
readToken();
if(list.count == 0){
return scope;
}
exp = newExpressionFromList(list, ret_values);
switch(exp->type){
case EXP_TYPE_CODE_LIST:
{
scope->list.swap(exp->list);
allocator->deleteObj(exp);
break;
}
// no break
default:
scope->list.add(exp OS_DBG_FILEPOS);
}
return scope;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectObjectExpression(Scope * scope, const Params& org_p, bool allow_finish_exp)
{
OS_ASSERT(recent_token && recent_token->type == Tokenizer::BEGIN_CODE_BLOCK);
struct Lib {
Compiler * compiler;
Expression * obj_exp;
Expression * finishValue(Scope * scope, const Params& p, bool allow_finish_exp)
{
if(!allow_finish_exp){
return obj_exp;
}
return compiler->finishValueExpression(scope, obj_exp, Params(p).setAllowAssign(false).setAllowAutoCall(false));
}
void * malloc(int size OS_DBG_FILEPOS_DECL)
{
return compiler->malloc(size OS_DBG_FILEPOS_PARAM);
}
Lib(Compiler * p_compiler, int active_locals)
{
compiler = p_compiler;
obj_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT, compiler->recent_token);
obj_exp->ret_values = 1;
}
Expression * error()
{
compiler->allocator->deleteObj(obj_exp);
return NULL;
}
Expression * error(ErrorType err, TokenData * token)
{
compiler->setError(err, token);
return error();
}
Expression * error(TokenType err, TokenData * token)
{
compiler->setError(err, token);
return error();
}
} lib(this, scope->function->num_locals);
Params p = Params().setAllowBinaryOperator(true);
// TokenData * name_token, * save_token;
for(readToken();;){
Expression * exp = NULL;
if(!recent_token){
return lib.error(ERROR_SYNTAX, recent_token);
}
if(recent_token->type == Tokenizer::END_CODE_BLOCK){
readToken();
return lib.finishValue(scope, org_p, allow_finish_exp);
}
TokenData * name_token = recent_token;
if(name_token->type == Tokenizer::BEGIN_ARRAY_BLOCK){
readToken();
TokenData * save_token = recent_token;
exp = expectSingleExpression(scope, p);
if(!exp){
return lib.error();
}
if(exp->ret_values < 1){
allocator->deleteObj(exp);
return lib.error(ERROR_EXPECT_VALUE, save_token);
}
exp = expectExpressionValues(exp, 1);
if(!recent_token || recent_token->type != Tokenizer::END_ARRAY_BLOCK){
allocator->deleteObj(exp);
return lib.error(Tokenizer::END_ARRAY_BLOCK, recent_token);
}
if(!readToken() || (recent_token->type != Tokenizer::OPERATOR_COLON && recent_token->type != Tokenizer::OPERATOR_ASSIGN)){
allocator->deleteObj(exp);
return lib.error(Tokenizer::OPERATOR_COLON, recent_token);
}
save_token = readToken();
Expression * exp2 = expectSingleExpression(scope, p);
if(!exp2){
return isError() ? lib.error() : lib.error(ERROR_EXPECT_EXPRESSION, save_token);
}
exp2 = expectExpressionValues(exp2, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT_SET_BY_EXP, name_token, exp, exp2 OS_DBG_FILEPOS);
}else if(isNextToken(Tokenizer::OPERATOR_COLON) || isNextToken(Tokenizer::OPERATOR_ASSIGN)){
ExpressionType exp_type = EXP_TYPE_OBJECT_SET_BY_NAME;
switch(name_token->type){
case Tokenizer::STRING:
case Tokenizer::NAME:
break;
case Tokenizer::NUMBER:
if(name_token->getFloat() != (OS_FLOAT)(OS_INT)name_token->getFloat()){
// use it as EXP_TYPE_OBJECT_SET_BY_NAME
break;
}
exp_type = EXP_TYPE_OBJECT_SET_BY_INDEX;
break;
default:
return lib.error(ERROR_SYNTAX, name_token);
}
readToken(); // skip OPERATOR_COLON
TokenData * save_token = readToken();
exp = expectSingleExpression(scope, p);
if(!exp){
return isError() ? lib.error() : lib.error(ERROR_EXPECT_EXPRESSION, save_token);
}
exp = expectExpressionValues(exp, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp_type, name_token, exp OS_DBG_FILEPOS);
}else{
exp = expectSingleExpression(scope, p);
if(!exp){
return isError() ? lib.error() : lib.error(ERROR_EXPECT_EXPRESSION, name_token);
}
exp = expectExpressionValues(exp, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT_SET_BY_AUTO_INDEX, name_token, exp OS_DBG_FILEPOS);
}
OS_ASSERT(exp);
lib.obj_exp->list.add(exp OS_DBG_FILEPOS);
if(recent_token && recent_token->type == Tokenizer::END_CODE_BLOCK){
readToken();
return lib.finishValue(scope, org_p, allow_finish_exp);
}
#if 11
if(!recent_token){
return lib.error(Tokenizer::END_CODE_BLOCK, recent_token);
}
switch(recent_token->type){
case Tokenizer::PARAM_SEPARATOR:
case Tokenizer::CODE_SEPARATOR:
readToken();
}
#else
if(!recent_token || (recent_token->type != Tokenizer::PARAM_SEPARATOR
&& recent_token->type != Tokenizer::CODE_SEPARATOR)){
return lib.error(Tokenizer::PARAM_SEPARATOR, recent_token);
}
readToken();
#endif
}
return NULL; // shut up compiler
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectArrayExpression(Scope * scope, const Params& __p)
{
Params next_p = Params(__p).setAllowAssign(false).setAllowAutoCall(false);
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_ARRAY, recent_token);
params->ret_values = 1;
readToken();
if(recent_token && recent_token->type == Tokenizer::END_ARRAY_BLOCK){
readToken();
return finishValueExpression(scope, params, next_p);
}
Params p = Params().setAllowBinaryOperator(true);
for(;;){
Expression * exp = expectSingleExpression(scope, p);
if(!exp){
if(isError()){
allocator->deleteObj(params);
return NULL;
}
if(!recent_token || recent_token->type != Tokenizer::END_ARRAY_BLOCK){
setError(Tokenizer::END_ARRAY_BLOCK, recent_token);
allocator->deleteObj(params);
return NULL;
}
readToken();
return finishValueExpression(scope, params, next_p);
}
exp = expectExpressionValues(exp, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_OBJECT_SET_BY_AUTO_INDEX, exp->token, exp OS_DBG_FILEPOS);
params->list.add(exp OS_DBG_FILEPOS);
if(recent_token && recent_token->type == Tokenizer::END_ARRAY_BLOCK){
readToken();
return finishValueExpression(scope, params, next_p);
}
#if 11
if(!recent_token){
setError(Tokenizer::END_ARRAY_BLOCK, recent_token);
allocator->deleteObj(params);
return NULL;
}
switch(recent_token->type){
case Tokenizer::PARAM_SEPARATOR:
case Tokenizer::CODE_SEPARATOR:
readToken();
}
#else
if(!recent_token || (recent_token->type != Tokenizer::PARAM_SEPARATOR
&& recent_token->type != Tokenizer::CODE_SEPARATOR)){
setError(Tokenizer::PARAM_SEPARATOR, recent_token);
allocator->deleteObj(params);
return NULL;
}
readToken();
#endif
}
return NULL; // shut up compiler
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectParamsExpression(Scope * scope)
{
struct Lib
{
static Expression * calcParamsExpression(Compiler * compiler, Scope * scope, Expression * params)
{
if(params->list.count > 1){
for(int i = 0; i < params->list.count; i++){
params->list[i] = compiler->expectExpressionValues(params->list[i], 1);
}
params->ret_values = params->list.count;
}else if(params->list.count == 1){
params->ret_values = params->list[0]->ret_values;
}
return params;
}
};
// OS_ASSERT(recent_token->type == Tokenizer::PARAM_SEPARATOR);
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, recent_token);
bool is_dim = recent_token->type == Tokenizer::BEGIN_ARRAY_BLOCK;
TokenType end_exp_type = is_dim ? Tokenizer::END_ARRAY_BLOCK : Tokenizer::END_BRACKET_BLOCK;
readToken();
if(recent_token && recent_token->type == end_exp_type){
readToken();
return Lib::calcParamsExpression(this, scope, params);
}
Params p = Params().setAllowBinaryOperator(true);
for(;;){
Expression * exp = expectSingleExpression(scope, p);
if(!exp){
if(isError()){
allocator->deleteObj(params);
return NULL;
}
if(!recent_token || recent_token->type != end_exp_type){
setError(end_exp_type, recent_token);
allocator->deleteObj(params);
return NULL;
}
readToken();
return Lib::calcParamsExpression(this, scope, params);
}
// exp = expectExpressionValues(exp, 1);
params->list.add(exp OS_DBG_FILEPOS);
// params->ret_values += exp->ret_values;
if(recent_token && (recent_token->type == Tokenizer::PARAM_SEPARATOR || recent_token->type == Tokenizer::CODE_SEPARATOR)){
readToken();
}
if(recent_token && recent_token->type == end_exp_type){
readToken();
return Lib::calcParamsExpression(this, scope, params);
}
if(!recent_token){ // || recent_token->type != Tokenizer::PARAM_SEPARATOR){
// setError(Tokenizer::PARAM_SEPARATOR, recent_token);
setError(end_exp_type, recent_token);
allocator->deleteObj(params);
return NULL;
}
}
return NULL; // shut up compiler
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectBracketExpression(Scope * scope, const Params& p)
{
OS_ASSERT(recent_token && recent_token->type == Tokenizer::BEGIN_BRACKET_BLOCK);
readToken();
Expression * exp = expectSingleExpression(scope, Params()
.setAllowBinaryOperator(true)
.setAllowCall(true)
.setAllowAutoCall(true));
if(!exp){
return NULL;
}
exp = newSingleValueExpression(exp);
OS_ASSERT(exp->ret_values == 1);
if(!recent_token){
setError(Tokenizer::END_BRACKET_BLOCK, recent_token);
allocator->deleteObj(exp);
return NULL;
}
switch(recent_token->type){
case Tokenizer::END_BRACKET_BLOCK:
readToken();
return finishValueExpression(scope, exp, p);
}
setError(Tokenizer::END_BRACKET_BLOCK, recent_token);
allocator->deleteObj(exp);
return NULL;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectExtendsExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_extends);
TokenData * save_token = recent_token;
if(!expectToken()){
return NULL;
}
Params p;
Expression * exp = expectSingleExpression(scope, p);
if(!exp){
return NULL;
}
if(exp->type == EXP_TYPE_CALL_AUTO_PARAM){
OS_ASSERT(exp->list.count == 2);
Expression * params = exp->list[1];
OS_ASSERT(params->type == EXP_TYPE_PARAMS && params->list.count == 1);
exp->list[1] = params->list[0];
allocator->vectorClear(params->list);
allocator->deleteObj(params);
exp->type = EXP_TYPE_EXTENDS;
exp->ret_values = 1;
return exp;
}
Expression * exp2 = expectSingleExpression(scope, p);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
exp = expectExpressionValues(exp, 1);
exp2 = expectExpressionValues(exp2, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_EXTENDS, save_token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::finishQuestionOperator(Scope * scope, TokenData * token, Expression * exp, Expression * exp2)
{
// OS_ASSERT(recent_token && recent_token->type == Tokenizer::OPERATOR_COLON);
ungetToken();
if(!expectToken(Tokenizer::OPERATOR_COLON)){
allocator->deleteObj(exp);
allocator->deleteObj(exp2);
return NULL;
}
if(!expectToken()){
return NULL;
}
Expression * exp3 = expectSingleExpression(scope, Params().setAllowBinaryOperator(true));
if(!exp3){
allocator->deleteObj(exp);
allocator->deleteObj(exp2);
return NULL;
}
exp = expectExpressionValues(exp, 1);
exp2 = expectExpressionValues(exp2, 1);
exp3 = expectExpressionValues(exp3, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_QUESTION, token, exp, exp2, exp3 OS_DBG_FILEPOS);
exp->ret_values = 1;
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectCloneExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_clone);
TokenData * save_token = recent_token;
if(!expectToken()){
return NULL;
}
Expression * exp = expectSingleExpression(scope, Params());
if(!exp){
return NULL;
}
exp = expectExpressionValues(exp, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CLONE, save_token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectDeleteExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_delete);
TokenData * save_token = recent_token;
if(!expectToken()){
return NULL;
}
Expression * exp = expectSingleExpression(scope, Params());
if(!exp){
return NULL;
}
if(exp->type == EXP_TYPE_INDIRECT){
OS_ASSERT(exp->list.count == 2);
Expression * field = exp->list[1];
if(field->type == EXP_TYPE_NAME){
field->type = EXP_TYPE_CONST_STRING;
}
exp->type = EXP_TYPE_DELETE;
exp->ret_values = 0;
return exp;
}
if(exp->type == EXP_TYPE_CALL_DIM){
OS_ASSERT(exp->list.count == 2);
Expression * params = exp->list[1];
if(params->list.count == 1){
exp->list[1] = params->list[0];
allocator->vectorClear(params->list);
allocator->deleteObj(params);
exp->type = EXP_TYPE_DELETE;
exp->ret_values = 0;
return exp;
}
Expression * object = exp->list[0];
String method_name = !params->list.count ? allocator->core->strings->__delempty : allocator->core->strings->__deldim;
TokenData * token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), method_name, Tokenizer::NAME, object->token->line, object->token->pos);
Expression * exp_method_name = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_STRING, token);
exp_method_name->ret_values = 1;
token->release();
Expression * indirect = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_INDIRECT, object->token, object, exp_method_name OS_DBG_FILEPOS);
exp->list[0] = indirect;
exp->type = EXP_TYPE_CALL;
exp->ret_values = 1;
return exp;
}
setError(ERROR_SYNTAX, exp->token);
allocator->deleteObj(exp);
return NULL;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectValueOfExpression(Scope * scope, ExpressionType exp_type)
{
OS_ASSERT(recent_token);
TokenData * save_token = recent_token;
if(!expectToken()){
return NULL;
}
Expression * exp = expectSingleExpression(scope, Params());
if(!exp){
return NULL;
}
exp = expectExpressionValues(exp, 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp_type, save_token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectFunctionExpression(Scope * parent)
{
Scope * scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(parent, EXP_TYPE_FUNCTION, recent_token);
scope->function = scope;
scope->ret_values = 1;
Expression * name_exp = NULL;
if(isNextToken(Tokenizer::NAME)){
TokenData * token = readToken();
if(isNextToken(Tokenizer::NAME)){
String prefix(allocator);
if(token->str == allocator->core->strings->syntax_get){
prefix = allocator->core->strings->__getAt;
}else if(token->str == allocator->core->strings->syntax_set){
prefix = allocator->core->strings->__setAt;
}else{
setError(ERROR_EXPECT_GET_OR_SET, token);
allocator->deleteObj(name_exp);
allocator->deleteObj(scope);
return NULL;
}
token = readToken();
token->str = String(allocator, prefix, token->str);
name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, token);
}else{
name_exp = expectSingleExpression(parent, Params().setAllowCall(false));
if(!name_exp || !name_exp->isWriteable()){
setError(ERROR_EXPECT_WRITEABLE, token);
allocator->deleteObj(name_exp);
allocator->deleteObj(scope);
return NULL;
}
ungetToken();
}
}
if(!expectToken(Tokenizer::BEGIN_BRACKET_BLOCK)){
allocator->deleteObj(scope);
return NULL;
}
for(;;){
if(!readToken()){
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(scope);
return NULL;
}
switch(recent_token->type){
case Tokenizer::END_BRACKET_BLOCK:
break;
case Tokenizer::NAME:
// scope->list.add(new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, recent_token));
scope->addLocalVar(recent_token->str);
scope->num_params++;
if(!readToken()){
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(scope);
return NULL;
}
if(recent_token->type == Tokenizer::END_BRACKET_BLOCK){
break;
}
if(recent_token->type == Tokenizer::PARAM_SEPARATOR){
continue;
}
#if 11
ungetToken();
continue;
#else
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(scope);
return NULL;
#endif
default:
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(scope);
return NULL;
}
break;
}
OS_ASSERT(recent_token && recent_token->type == Tokenizer::END_BRACKET_BLOCK);
if(!expectToken(Tokenizer::BEGIN_CODE_BLOCK)){
allocator->deleteObj(scope);
return NULL;
}
scope->addStdVars();
scope = expectCodeExpression(scope);
if(!scope || !name_exp){
return scope;
}
return newBinaryExpression(parent, EXP_TYPE_ASSIGN, name_exp->token, name_exp, scope);
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectVarExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_var);
if(!expectToken(Tokenizer::NAME)){
return NULL;
}
Expression * name_exp;
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, recent_token);
exp->ret_values = 1;
if(recent_token->str == allocator->core->strings->syntax_function){
if(!expectToken(Tokenizer::NAME)){
allocator->deleteObj(exp);
return NULL;
}
TokenData * name_token;
if(recent_token->str == allocator->core->strings->syntax_get || recent_token->str == allocator->core->strings->syntax_set){
bool is_getter = recent_token->str == allocator->core->strings->syntax_get;
if(!expectToken(Tokenizer::NAME)){
allocator->deleteObj(exp);
return NULL;
}
if(!isVarNameValid(recent_token->str)){
setError(ERROR_VAR_NAME, recent_token);
allocator->deleteObj(exp);
return NULL;
}
if(!expectToken(Tokenizer::BEGIN_BRACKET_BLOCK)){
allocator->deleteObj(exp);
return NULL;
}
ungetToken();
ungetToken();
name_token = tokenizer->removeToken(next_token_index-1); name_token->release();
name_token = tokenizer->removeToken(next_token_index-1);
//name_token->str =
if(is_getter){
name_token->str = String(allocator, allocator->core->strings->__getAt, name_token->str);
}else{
name_token->str = String(allocator, allocator->core->strings->__setAt, name_token->str);
}
}else{
if(!isVarNameValid(recent_token->str)){
setError(ERROR_VAR_NAME, recent_token);
allocator->deleteObj(exp);
return NULL;
}
if(!expectToken(Tokenizer::BEGIN_BRACKET_BLOCK)){
allocator->deleteObj(exp);
return NULL;
}
ungetToken();
name_token = tokenizer->removeToken(next_token_index-1);
}
name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, name_token);
name_exp->ret_values = 1;
name_token->release();
allocator->deleteObj(exp);
// exp->list.add(name_exp OS_DBG_FILEPOS);
// exp->ret_values++;
ungetToken(); // return to function
Expression * func_exp = expectFunctionExpression(scope);
if(!func_exp){
allocator->deleteObj(exp);
return NULL;
}
OS_ASSERT(func_exp->type == EXP_TYPE_FUNCTION);
exp = newBinaryExpression(scope, EXP_TYPE_ASSIGN, name_exp->token, name_exp, func_exp);
}else{
for(;;){
if(!isVarNameValid(recent_token->str)){
setError(ERROR_VAR_NAME, recent_token);
allocator->deleteObj(exp);
return NULL;
}
name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, recent_token);
name_exp->ret_values = 1;
exp->list.add(name_exp OS_DBG_FILEPOS);
exp->ret_values++;
if(!readToken() || recent_token->type != Tokenizer::PARAM_SEPARATOR){
break;
}
if(!expectToken(Tokenizer::NAME)){
allocator->deleteObj(exp);
return NULL;
}
}
/*
while(readToken()){
if(recent_token->type != Tokenizer::PARAM_SEPARATOR){
break;
}
if(!expectToken(Tokenizer::NAME)){
allocator->deleteObj(exp);
return NULL;
}
if(!isVarNameValid(recent_token->str)){
setError(ERROR_VAR_NAME, recent_token);
allocator->deleteObj(exp);
return NULL;
}
name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, recent_token);
name_exp->ret_values = 1;
exp->list.add(name_exp OS_DBG_FILEPOS);
exp->ret_values++;
}
*/
if(recent_token && recent_token->type == Tokenizer::OPERATOR_ASSIGN){
bool is_finished;
exp = finishBinaryOperator(scope, getOpcodeLevel(exp->type), exp, Params().setAllowParams(true).setAllowInOperator(true), is_finished);
OS_ASSERT(is_finished);
}
}
// Expression * exp = expectSingleExpression(scope, Params().setAllowParams(true).setAllowAssign(true)); // false, true, false, true, false);
Expression * ret_exp = exp;
while(exp){
switch(exp->type){
case EXP_TYPE_PARAMS:
{
Expression * params = exp;
for(int i = 0; i < params->list.count; i++){
exp = params->list[i];
OS_ASSERT(exp->type == EXP_TYPE_NAME);
if(exp->type == EXP_TYPE_NAME){
if(findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, false)){
// setError(ERROR_VAR_ALREADY_EXIST, exp->token);
// allocator->deleteObj(ret_exp);
// return NULL;
// OS_ASSERT(true);
}else{
scope->addLocalVar(exp->token->str, exp->local_var);
}
OS_ASSERT(exp->local_var.up_count == 0);
exp->type = EXP_TYPE_NEW_LOCAL_VAR;
exp->ret_values = 0;
}
}
params->ret_values = 0;
return params;
}
case EXP_TYPE_SET_LOCAL_VAR:
for(;;){
if(exp->local_var.up_scope_count == 0){
// setError(ERROR_VAR_ALREADY_EXIST, exp->token);
// allocator->deleteObj(ret_exp);
// return NULL;
OS_ASSERT(true);
}else{
OS_ASSERT(!findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, false));
scope->addLocalVar(exp->token->str, exp->local_var);
}
OS_ASSERT(exp->list.count == 1);
exp = exp->list[0];
switch(exp->type){
case EXP_TYPE_SET_ENV_VAR:
case EXP_TYPE_SET_LOCAL_VAR:
break;
default:
return ret_exp;
}
break;
}
break;
case EXP_TYPE_SET_ENV_VAR:
for(;;){
OS_ASSERT(!findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, false));
scope->addLocalVar(exp->token->str, exp->local_var);
exp->type = EXP_TYPE_SET_LOCAL_VAR;
OS_ASSERT(exp->list.count == 1);
exp = exp->list[0];
switch(exp->type){
case EXP_TYPE_SET_ENV_VAR:
case EXP_TYPE_SET_LOCAL_VAR:
break;
default:
return ret_exp;
}
break;
}
break;
case EXP_TYPE_NAME:
if(findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, false)){
// setError(ERROR_VAR_ALREADY_EXIST, exp->token);
// allocator->deleteObj(ret_exp);
// return NULL;
OS_ASSERT(true);
}else{
scope->addLocalVar(exp->token->str, exp->local_var);
}
OS_ASSERT(exp->local_var.up_count == 0);
exp->type = EXP_TYPE_NEW_LOCAL_VAR;
exp->ret_values = 0;
return ret_exp;
default:
return ret_exp;
}
}
return ret_exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectForExpression(Scope * parent)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_for);
Scope * scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(parent, EXP_TYPE_SCOPE, recent_token);
if(!expectToken(Tokenizer::BEGIN_BRACKET_BLOCK) || !expectToken()){
allocator->deleteObj(scope);
return NULL;
}
// Expression * exp = expectSingleExpression(scope, true); // , true, true, true, true, true);
Expression * exp = expectSingleExpression(scope, Params()
.setAllowAssign(true)
.setAllowAutoCall(true)
.setAllowBinaryOperator(true)
.setAllowParams(true)
.setAllowVarDecl(true)
.setAllowNopResult(true)
.setAllowInOperator(false));
if(!exp){
allocator->deleteObj(scope);
return NULL;
}
if(!recent_token){
setError(ERROR_EXPECT_TOKEN, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(exp);
return NULL;
}
if(recent_token->type == Tokenizer::NAME && (exp->type == EXP_TYPE_PARAMS || exp->type == EXP_TYPE_NEW_LOCAL_VAR || exp->type == EXP_TYPE_NAME)){
if(recent_token->str != allocator->core->strings->syntax_in){
setError(allocator->core->strings->syntax_in, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(exp);
return NULL;
}
ExpressionList vars(allocator);
if(exp->type == EXP_TYPE_PARAMS){
vars.swap(exp->list);
allocator->deleteObj(exp);
}else{
vars.add(exp OS_DBG_FILEPOS);
}
exp = NULL;
for(int i = 0; i < vars.count; i++){
OS_ASSERT(vars[i]->type == EXP_TYPE_NAME || vars[i]->type == EXP_TYPE_NEW_LOCAL_VAR);
Expression * name_exp = vars[i];
if(name_exp->type == EXP_TYPE_NAME){
/*
scope->addLocalVar(name_exp->token->str, name_exp->local_var);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->type = EXP_TYPE_NEW_LOCAL_VAR;
*/
name_exp->type = EXP_TYPE_NOP;
}
}
if(!expectToken()){
allocator->deleteObj(scope);
return NULL;
}
exp = expectSingleExpression(scope, Params().setAllowBinaryOperator(true).setAllowAutoCall(true)); // true, false, false, false, true);
if(!recent_token || recent_token->type != Tokenizer::END_BRACKET_BLOCK){
setError(Tokenizer::END_BRACKET_BLOCK, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(exp);
return NULL;
}
if(!exp->ret_values){
setError(ERROR_EXPECT_VALUE, exp->token);
allocator->deleteObj(scope);
allocator->deleteObj(exp);
return NULL;
}
exp = expectExpressionValues(exp, 1);
if(!expectToken()){
allocator->deleteObj(scope);
allocator->deleteObj(exp);
return NULL;
}
Scope * loop_scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(scope, EXP_TYPE_LOOP_SCOPE, recent_token);
Expression * body_exp = expectSingleExpression(loop_scope, true, true);
/* if(recent_token->type == Tokenizer::BEGIN_CODE_BLOCK){
body_exp = expectCodeExpression(loop_scope);
}else{
body_exp = expectSingleExpression(loop_scope, true);
} */
if(!body_exp){
allocator->deleteObj(scope);
allocator->deleteObj(exp);
allocator->deleteObj(loop_scope);
return NULL;
}
body_exp = expectExpressionValues(body_exp, 0);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL_METHOD, exp->token, exp OS_DBG_FILEPOS);
{
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, exp->token);
String method_name = allocator->core->strings->__iter;
TokenData * token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), method_name, Tokenizer::NAME, exp->token->line, exp->token->pos);
Expression * exp_method_name = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_STRING, token);
exp_method_name->ret_values = 1;
token->release();
params->list.add(exp_method_name OS_DBG_FILEPOS);
params->ret_values = 1;
exp->list.add(params OS_DBG_FILEPOS);
}
exp = expectExpressionValues(exp, vars.count + 1);
int num_locals = vars.count;
// ExpressionList temp_vars(allocator);
const int temp_count = 2;
const OS_CHAR * temp_names[temp_count] = {
OS_TEXT("#func"), /*OS_TEXT("#state"), OS_TEXT("#state2"), */ OS_TEXT("#valid")
};
for(int i = 0; i < temp_count; i++){
String name(allocator, temp_names[i]);
TokenData * token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), name, Tokenizer::NAME, exp->token->line, exp->token->pos);
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NEW_LOCAL_VAR, token);
// name_exp->ret_values = 1;
vars.add(name_exp OS_DBG_FILEPOS);
token->release();
scope->addLocalVar(name, name_exp->local_var);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->local_var.type = LOCAL_TEMP;
}
ExpressionList list(allocator);
// var func, state, state2 = (in_exp).__iter()
{
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, exp->token);
for(int i = num_locals; i < vars.count-1; i++){
Expression * var_exp = vars[i];
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, var_exp->token);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->ret_values = 1;
params->list.add(name_exp OS_DBG_FILEPOS);
}
params->ret_values = params->list.count;
String assing_operator(allocator, OS_TEXT("="));
TokenData * assign_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), assing_operator, Tokenizer::OPERATOR_ASSIGN, exp->token->line, exp->token->pos);
exp = newBinaryExpression(scope, EXP_TYPE_ASSIGN, assign_token, params, exp);
OS_ASSERT(exp && exp->type == EXP_TYPE_SET_LOCAL_VAR && !exp->ret_values);
assign_token->release();
list.add(exp OS_DBG_FILEPOS); exp = NULL;
}
/*
begin loop
var valid, k, v = func(state, state2)
if(!valid) break
body_exp
end loop
*/
list.add(loop_scope OS_DBG_FILEPOS);
{
// var valid, k, v
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, loop_scope->token);
for(int i = 0; i < num_locals+1; i++){
Expression * var_exp = !i ? vars.lastElement() : vars[i-1];
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, var_exp->token);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->ret_values = 1;
params->list.add(name_exp OS_DBG_FILEPOS);
}
params->ret_values = params->list.count;
// func(state, state2)
Expression * call_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL, loop_scope->token);
{
Expression * var_exp = vars[num_locals]; // func
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, var_exp->token);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->ret_values = 1;
call_exp->list.add(name_exp OS_DBG_FILEPOS);
Expression * params = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, loop_scope->token);
for(int i = num_locals+1; i < vars.count-1; i++){
Expression * var_exp = vars[i];
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, var_exp->token);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->ret_values = 1;
params->list.add(name_exp OS_DBG_FILEPOS);
}
params->ret_values = params->list.count;
call_exp->list.add(params OS_DBG_FILEPOS);
}
call_exp->ret_values = params->list.count;
// var valid, k, v = func(state, state2)
String assing_operator(allocator, OS_TEXT("="));
TokenData * assign_token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(tokenizer->getTextData(), assing_operator,
Tokenizer::OPERATOR_ASSIGN, loop_scope->token->line, loop_scope->token->pos);
exp = newBinaryExpression(scope, EXP_TYPE_ASSIGN, assign_token, params, call_exp);
OS_ASSERT(exp && exp->type == EXP_TYPE_SET_LOCAL_VAR && !exp->ret_values);
assign_token->release();
loop_scope->list.add(exp OS_DBG_FILEPOS); exp = NULL;
}
// if(!valid) break
{
Expression * var_exp = vars.lastElement(); // valid var
Expression * name_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, var_exp->token);
OS_ASSERT(scope->function);
name_exp->active_locals = scope->function->num_locals;
name_exp->ret_values = 1;
Expression * not_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_LOGIC_NOT, loop_scope->token, name_exp OS_DBG_FILEPOS);
not_exp->ret_values = 1;
Expression * break_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_BREAK, loop_scope->token);
Expression * if_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_IF, loop_scope->token, not_exp, break_exp OS_DBG_FILEPOS);
loop_scope->list.add(if_exp OS_DBG_FILEPOS);
}
loop_scope->list.add(body_exp OS_DBG_FILEPOS);
// assemble all exps
scope->list.swap(vars);
scope->list.add(newExpressionFromList(list, 0) OS_DBG_FILEPOS);
return scope;
}
Expression * pre_exp = exp;
if(recent_token->type != Tokenizer::CODE_SEPARATOR){
setError(Tokenizer::CODE_SEPARATOR, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
return NULL;
}
readToken();
Expression * bool_exp;
if(recent_token->type == Tokenizer::CODE_SEPARATOR){
bool_exp = NULL;
}else{
bool_exp = expectSingleExpression(scope, Params().setAllowAutoCall(true).setAllowBinaryOperator(true));
if(!bool_exp){
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
return NULL;
}
}
if(bool_exp && !bool_exp->ret_values){
setError(ERROR_EXPECT_VALUE, bool_exp->token);
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
allocator->deleteObj(bool_exp);
return NULL;
}
if(recent_token->type != Tokenizer::CODE_SEPARATOR){
setError(Tokenizer::CODE_SEPARATOR, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
allocator->deleteObj(bool_exp);
return NULL;
}
readToken();
Expression * post_exp = expectSingleExpression(scope, Params()
.setAllowAssign(true)
.setAllowAutoCall(true)
.setAllowBinaryOperator(true)
.setAllowNopResult(true));
if(!post_exp){
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
allocator->deleteObj(bool_exp);
return NULL;
}
if(recent_token->type != Tokenizer::END_BRACKET_BLOCK){
setError(Tokenizer::END_BRACKET_BLOCK, recent_token);
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
allocator->deleteObj(bool_exp);
allocator->deleteObj(post_exp);
return NULL;
}
readToken();
Scope * loop_scope = new (malloc(sizeof(Scope) OS_DBG_FILEPOS)) Scope(scope, EXP_TYPE_LOOP_SCOPE, recent_token);
Expression * body_exp = expectSingleExpression(loop_scope, true, true);
if(!body_exp){
allocator->deleteObj(scope);
allocator->deleteObj(pre_exp);
allocator->deleteObj(bool_exp);
allocator->deleteObj(post_exp);
allocator->deleteObj(loop_scope);
return NULL;
}
if(bool_exp){
bool_exp = expectExpressionValues(bool_exp, 1);
Expression * not_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_LOGIC_NOT, bool_exp->token, bool_exp OS_DBG_FILEPOS);
not_exp->ret_values = 1;
Expression * break_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_BREAK, bool_exp->token);
Expression * if_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_IF, bool_exp->token, not_exp, break_exp OS_DBG_FILEPOS);
loop_scope->list.add(if_exp OS_DBG_FILEPOS);
}
body_exp = expectExpressionValues(body_exp, 0);
loop_scope->list.add(body_exp OS_DBG_FILEPOS);
post_exp = expectExpressionValues(post_exp, 0);
loop_scope->list.add(post_exp OS_DBG_FILEPOS);
scope->list.add(pre_exp OS_DBG_FILEPOS);
scope->list.add(loop_scope OS_DBG_FILEPOS);
return scope;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectDebugLocalsExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_debuglocals);
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_DEBUG_LOCALS, recent_token);
exp->ret_values = 1;
readToken();
return exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectIfExpression(Scope * scope)
{
OS_ASSERT(recent_token && (recent_token->str == allocator->core->strings->syntax_if
|| recent_token->str == allocator->core->strings->syntax_elseif));
if(!expectToken(Tokenizer::BEGIN_BRACKET_BLOCK) || !expectToken()){
return NULL;
}
TokenData * token = recent_token;
Expression * if_exp = expectSingleExpression(scope, Params().setAllowBinaryOperator(true).setAllowNopResult(true));
if(!if_exp){
return NULL;
}
if(if_exp->ret_values < 1){
setError(ERROR_EXPECT_VALUE, token);
allocator->deleteObj(if_exp);
return NULL;
}
if_exp = expectExpressionValues(if_exp, 1);
if(!recent_token || recent_token->type != Tokenizer::END_BRACKET_BLOCK){
setError(Tokenizer::END_BRACKET_BLOCK, recent_token);
allocator->deleteObj(if_exp);
return NULL;
}
if(!expectToken()){
allocator->deleteObj(if_exp);
return NULL;
}
if(!recent_token){
setError(ERROR_EXPECT_TOKEN, recent_token);
allocator->deleteObj(if_exp);
return NULL;
}
token = recent_token;
Expression * then_exp = expectSingleExpression(scope, true, true);
/* if(recent_token->type == Tokenizer::BEGIN_CODE_BLOCK){
then_exp = expectCodeExpression(scope);
}else{
then_exp = expectSingleExpression(scope, true);
} */
if(!then_exp){
allocator->deleteObj(if_exp);
return NULL;
}
then_exp = expectExpressionValues(then_exp, 0);
if(recent_token && recent_token->type == Tokenizer::NAME){
Expression * else_exp = NULL;
if(recent_token->str == allocator->core->strings->syntax_elseif){
if(!expectToken()){
allocator->deleteObj(if_exp);
allocator->deleteObj(then_exp);
return NULL;
}
else_exp = expectIfExpression(scope);
}else if(recent_token->str == allocator->core->strings->syntax_else){
if(!expectToken()){
allocator->deleteObj(if_exp);
allocator->deleteObj(then_exp);
return NULL;
}
token = recent_token;
else_exp = expectSingleExpression(scope, true, true);
/* if(recent_token->type == Tokenizer::BEGIN_CODE_BLOCK){
else_exp = expectCodeExpression(scope);
}else{
else_exp = expectSingleExpression(scope);
} */
}else{
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_IF, if_exp->token, if_exp, then_exp OS_DBG_FILEPOS);
}
if(!else_exp){
allocator->deleteObj(if_exp);
allocator->deleteObj(then_exp);
return NULL;
}
else_exp = expectExpressionValues(else_exp, 0);
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_IF, if_exp->token, if_exp, then_exp, else_exp OS_DBG_FILEPOS);
}
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_IF, if_exp->token, if_exp, then_exp OS_DBG_FILEPOS);
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectReturnExpression(Scope * scope)
{
OS_ASSERT(recent_token && recent_token->str == allocator->core->strings->syntax_return);
Expression * ret_exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_RETURN, recent_token);
if(!readToken()){
setError(ERROR_SYNTAX, recent_token);
allocator->deleteObj(ret_exp);
return NULL;
}
switch(recent_token->type){
case Tokenizer::END_ARRAY_BLOCK:
case Tokenizer::END_BRACKET_BLOCK:
case Tokenizer::END_CODE_BLOCK:
case Tokenizer::CODE_SEPARATOR:
return ret_exp;
}
Expression * exp = expectSingleExpression(scope, Params().setAllowBinaryOperator(true).setAllowParams(true));
if(!exp){
allocator->deleteObj(ret_exp);
return NULL;
}
if(exp->type == EXP_TYPE_PARAMS){
ret_exp->list.swap(exp->list);
ret_exp->ret_values = exp->ret_values;
allocator->deleteObj(exp);
}else{
ret_exp->list.add(exp OS_DBG_FILEPOS);
ret_exp->ret_values = exp->ret_values;
}
return ret_exp;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::newBinaryExpression(Scope * scope, ExpressionType exp_type, TokenData * token, Expression * left_exp, Expression * right_exp)
{
// OS_ASSERT(token->isTypeOf(Tokenizer::BINARY_OPERATOR));
if(left_exp->isConstValue() && right_exp->isConstValue()){
struct Lib {
Compiler * compiler;
TokenData * token;
void * malloc(int size OS_DBG_FILEPOS_DECL)
{
return compiler->malloc(size OS_DBG_FILEPOS_PARAM);
}
Expression * newExpression(const String& str, Expression * left_exp, Expression * right_exp)
{
token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(token->text_data, str, Tokenizer::STRING, token->line, token->pos);
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_STRING, token);
exp->ret_values = 1;
token->release();
compiler->allocator->deleteObj(left_exp);
compiler->allocator->deleteObj(right_exp);
return exp;
}
Expression * newExpression(OS_FLOAT val, Expression * left_exp, Expression * right_exp)
{
token = new (malloc(sizeof(TokenData) OS_DBG_FILEPOS)) TokenData(token->text_data, String(compiler->allocator, val), Tokenizer::NUMBER, token->line, token->pos);
token->setFloat(val);
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NUMBER, token);
exp->ret_values = 1;
token->release();
compiler->allocator->deleteObj(left_exp);
compiler->allocator->deleteObj(right_exp);
return exp;
}
Expression * newExpression(OS_INT val, Expression * left_exp, Expression * right_exp)
{
return newExpression((OS_FLOAT)val, left_exp, right_exp);
}
Expression * switchExpression(bool ret_left, Expression * left_exp, Expression * right_exp)
{
if(ret_left){
compiler->allocator->deleteObj(right_exp);
return left_exp;
}
compiler->allocator->deleteObj(left_exp);
return right_exp;
}
} lib = {this, token};
switch(exp_type){
case EXP_TYPE_CONCAT: // ..
return lib.newExpression(String(allocator->core->newStringValue(left_exp->toString(), right_exp->toString())), left_exp, right_exp);
// case EXP_TYPE_ANY_PARAMS: // ...
/*
case EXP_TYPE_LOGIC_AND: // &&
return lib.switchExpression(left_exp->toInt() == 0, left_exp, right_exp);
case EXP_TYPE_LOGIC_OR: // ||
return lib.switchExpression(left_exp->toInt() != 0, left_exp, right_exp);
*/
/*
case EXP_TYPE_LOGIC_PTR_EQ: // ===
case EXP_TYPE_LOGIC_PTR_NE: // !==
case EXP_TYPE_LOGIC_EQ: // ==
case EXP_TYPE_LOGIC_NE: // !=
case EXP_TYPE_LOGIC_GE: // >=
case EXP_TYPE_LOGIC_LE: // <=
case EXP_TYPE_LOGIC_GREATER: // >
case EXP_TYPE_LOGIC_LESS: // <
*/
// case EXP_TYPE_LOGIC_NOT: // !
// case EXP_TYPE_INC: // ++
// case EXP_TYPE_DEC: // --
// case EXP_TYPE_QUESTION: // ?
// case EXP_TYPE_COLON: // :
case EXP_TYPE_BIT_AND: // &
return lib.newExpression(left_exp->toInt() & right_exp->toInt(), left_exp, right_exp);
case EXP_TYPE_BIT_OR: // |
return lib.newExpression(left_exp->toInt() | right_exp->toInt(), left_exp, right_exp);
case EXP_TYPE_BIT_XOR: // ^
return lib.newExpression(left_exp->toInt() ^ right_exp->toInt(), left_exp, right_exp);
// case EXP_TYPE_BIT_NOT: // ~
case EXP_TYPE_ADD: // +
return lib.newExpression(left_exp->toNumber() + right_exp->toNumber(), left_exp, right_exp);
case EXP_TYPE_SUB: // -
return lib.newExpression(left_exp->toNumber() - right_exp->toNumber(), left_exp, right_exp);
case EXP_TYPE_MUL: // *
return lib.newExpression(left_exp->toNumber() * right_exp->toNumber(), left_exp, right_exp);
case EXP_TYPE_DIV: // /
return lib.newExpression(left_exp->toNumber() / right_exp->toNumber(), left_exp, right_exp);
case EXP_TYPE_MOD: // %
return lib.newExpression(OS_MATH_MOD_OPERATOR(left_exp->toNumber(), right_exp->toNumber()), left_exp, right_exp);
case EXP_TYPE_LSHIFT: // <<
return lib.newExpression(left_exp->toInt() << right_exp->toInt(), left_exp, right_exp);
case EXP_TYPE_RSHIFT: // >>
return lib.newExpression(left_exp->toInt() >> right_exp->toInt(), left_exp, right_exp);
case EXP_TYPE_POW: // **
return lib.newExpression(OS_MATH_POW_OPERATOR(left_exp->toNumber(), right_exp->toNumber()), left_exp, right_exp);
}
}
switch(exp_type){
case EXP_TYPE_QUESTION:
return finishQuestionOperator(scope, token, left_exp, right_exp);
case EXP_TYPE_ASSIGN:
{
if(left_exp->type != EXP_TYPE_PARAMS){
right_exp = expectExpressionValues(right_exp, 1);
return newAssingExpression(scope, left_exp, right_exp);
}
Expression * values_exp = expectExpressionValues(right_exp, left_exp->list.count);
for(int i = left_exp->list.count-1; i >= 0; i--){
OS_ASSERT(values_exp->ret_values > 0);
Expression * var_exp = left_exp->list[i];
left_exp->list.removeIndex(i); // left_exp is going to be deleted
values_exp = newAssingExpression(scope, var_exp, values_exp);
if(!values_exp){
break;
}
}
allocator->deleteObj(left_exp);
return values_exp;
}
}
if(left_exp->type == EXP_TYPE_PARAMS){
OS_ASSERT(right_exp->type != EXP_TYPE_PARAMS);
right_exp = expectExpressionValues(right_exp, 1);
left_exp->list.add(right_exp OS_DBG_FILEPOS);
left_exp->ret_values++;
return left_exp;
}
if(right_exp->type == EXP_TYPE_PARAMS){
Expression * params = right_exp;
OS_ASSERT(params->list.count > 0);
if(params->list.count == 1){
right_exp = params->list[0];
allocator->vectorClear(params->list);
allocator->deleteObj(params);
}else{
left_exp = expectExpressionValues(left_exp, 1);
allocator->vectorInsertAtIndex(params->list, 0, left_exp OS_DBG_FILEPOS);
params->ret_values++;
return params;
}
}
left_exp = expectExpressionValues(left_exp, 1);
right_exp = expectExpressionValues(right_exp, 1);
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp_type, token, left_exp, right_exp OS_DBG_FILEPOS);
exp->ret_values = exp_type == EXP_TYPE_PARAMS ? 2 : 1;
return exp;
}
bool OS::Core::Compiler::findLocalVar(LocalVarDesc& desc, Scope * scope, const String& name, int active_locals, bool all_scopes)
{
OS_ASSERT(scope);
for(int up_count = 0, up_scope_count = 0;;){
for(int i = scope->locals.count-1; i >= 0; i--){
const Scope::LocalVar& local_var = scope->locals[i];
if((up_count || local_var.index < active_locals) && local_var.name == name){
desc.index = local_var.index;
desc.up_count = up_count;
desc.up_scope_count = up_scope_count;
desc.type = i < scope->num_params ? LOCAL_PARAM : (name.toChar()[0] != OS_TEXT('#') ? LOCAL_GENERIC : LOCAL_TEMP);
return true;
}
}
if(scope->parent){
if(!all_scopes){
return false;
}
if(scope->type == EXP_TYPE_FUNCTION){
up_count++;
}
up_scope_count++;
scope = scope->parent;
continue;
}
break;
}
return false;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::newAssingExpression(Scope * scope, Expression * var_exp, Expression * value_exp)
{
switch(var_exp->type){
case EXP_TYPE_CALL_DIM:
{
Expression * name_exp = var_exp->list[0];
Expression * params = var_exp->list[1];
OS_ASSERT(params->type == EXP_TYPE_PARAMS);
allocator->vectorInsertAtIndex(var_exp->list, 0, value_exp OS_DBG_FILEPOS);
var_exp->type = EXP_TYPE_SET_DIM;
var_exp->ret_values = value_exp->ret_values-1;
return var_exp;
}
case EXP_TYPE_INDIRECT:
{
OS_ASSERT(var_exp->list.count == 2);
Expression * var_exp_left = var_exp->list[0];
switch(var_exp_left->type){
case EXP_TYPE_NAME:
{
OS_ASSERT(var_exp_left->ret_values == 1);
if(findLocalVar(var_exp_left->local_var, scope, var_exp_left->token->str, var_exp_left->active_locals, true)){
var_exp_left->type = EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE;
if(scope->function->max_up_count < var_exp_left->local_var.up_count){
scope->function->max_up_count = var_exp_left->local_var.up_count;
}
}else{
var_exp_left->type = EXP_TYPE_GET_ENV_VAR_AUTO_CREATE;
}
break;
}
}
ExpressionType exp_type = EXP_TYPE_SET_PROPERTY;
Expression * var_exp_right = var_exp->list[1];
switch(var_exp_right->type){
case EXP_TYPE_NAME:
OS_ASSERT(var_exp_right->ret_values == 1);
var_exp_right->type = EXP_TYPE_CONST_STRING;
break;
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
OS_ASSERT(false);
return NULL;
case EXP_TYPE_CALL_DIM:
OS_ASSERT(false);
return NULL;
}
Expression * exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(exp_type, var_exp->token, value_exp, var_exp_left, var_exp_right OS_DBG_FILEPOS);
exp->ret_values = value_exp->ret_values-1;
allocator->vectorClear(var_exp->list);
allocator->deleteObj(var_exp);
return exp;
}
break;
case EXP_TYPE_NAME:
if(findLocalVar(var_exp->local_var, scope, var_exp->token->str, var_exp->active_locals, true)){
var_exp->type = EXP_TYPE_SET_LOCAL_VAR;
if(scope->function->max_up_count < var_exp->local_var.up_count){
scope->function->max_up_count = var_exp->local_var.up_count;
}
}else{
var_exp->type = EXP_TYPE_SET_ENV_VAR;
}
var_exp->list.add(value_exp OS_DBG_FILEPOS);
var_exp->ret_values = value_exp->ret_values-1;
return var_exp;
default:
// OS_ASSERT(false);
if(!var_exp->isWriteable()){
setError(ERROR_EXPECT_WRITEABLE, var_exp->token);
allocator->deleteObj(var_exp);
allocator->deleteObj(value_exp);
return NULL;
}
}
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_ASSIGN, var_exp->token, var_exp, value_exp OS_DBG_FILEPOS);
}
OS::Core::Compiler::Expression * OS::Core::Compiler::finishBinaryOperator(Scope * scope, OpcodeLevel prev_level, Expression * exp,
const Params& _p, bool& is_finished)
{
TokenData * binary_operator = recent_token;
OS_ASSERT(binary_operator->isTypeOf(Tokenizer::BINARY_OPERATOR));
Params p = Params(_p)
.setAllowAssign(false)
.setAllowBinaryOperator(false)
.setAllowInOperator(_p.allow_in_operator)
// .setAllowParams(false)
.setAllowAutoCall(false) // binary_operator->type == Tokenizer::OPERATOR_ASSIGN)
.setAllowRootBlocks(false);
readToken();
Expression * exp2 = expectSingleExpression(scope, Params(p).setAllowParams(false)); // false, allow_param, false, false, false);
if(!exp2){
/* if(!isError()){
return exp;
} */
is_finished = true;
allocator->deleteObj(exp);
return NULL;
}
// exp2 = expectExpressionValues(exp2, 1);
if(recent_token && recent_token->type == Tokenizer::NAME){
if(recent_token->str == allocator->core->strings->syntax_in){
if(p.allow_in_operator){
recent_token->type = Tokenizer::OPERATOR_IN;
}
}else if(recent_token->str == allocator->core->strings->syntax_isprototypeof){
recent_token->type = Tokenizer::OPERATOR_ISPROTOTYPEOF;
}else if(recent_token->str == allocator->core->strings->syntax_is){
recent_token->type = Tokenizer::OPERATOR_IS;
}
}
if(!recent_token || !recent_token->isTypeOf(Tokenizer::BINARY_OPERATOR) || (!p.allow_params && recent_token->type == Tokenizer::PARAM_SEPARATOR)){
// return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(getExpressionType(binary_operator->type), binary_operator, exp, exp2);
is_finished = true;
return newBinaryExpression(scope, getExpressionType(binary_operator->type), binary_operator, exp, exp2);
}
ExpressionType left_exp_type = getExpressionType(binary_operator->type);
ExpressionType right_exp_type = getExpressionType(recent_token->type);
OpcodeLevel left_level = getOpcodeLevel(left_exp_type);
OpcodeLevel right_level = getOpcodeLevel(right_exp_type);
if(left_level == right_level){
// exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(left_exp_type, binary_operator, exp, exp2);
exp = newBinaryExpression(scope, left_exp_type, binary_operator, exp, exp2);
return finishBinaryOperator(scope, prev_level, exp, p, is_finished);
}
if(left_level > right_level){
// exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(left_exp_type, binary_operator, exp, exp2);
exp = newBinaryExpression(scope, left_exp_type, binary_operator, exp, exp2);
if(prev_level >= right_level){
is_finished = false;
return exp;
}
return finishBinaryOperator(scope, prev_level, exp, p, is_finished);
}
exp2 = finishBinaryOperator(scope, left_level, exp2, p, is_finished);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
exp = newBinaryExpression(scope, left_exp_type, binary_operator, exp, exp2);
if(is_finished){ // !recent_token || !recent_token->isTypeOf(Tokenizer::BINARY_OPERATOR)){
return exp;
}
return finishBinaryOperator(scope, prev_level, exp, p, is_finished);
}
OS::Core::Compiler::Expression * OS::Core::Compiler::finishValueExpressionNoAutoCall(Scope * scope, Expression * exp, const Params& p)
{
return finishValueExpression(scope, exp, Params(p).setAllowAutoCall(false));
}
OS::Core::Compiler::Expression * OS::Core::Compiler::finishValueExpressionNoNextCall(Scope * scope, Expression * exp, const Params& p)
{
if(recent_token && recent_token->type == Tokenizer::BEGIN_BRACKET_BLOCK){
return exp;
}
return finishValueExpression(scope, exp, Params(p).setAllowAutoCall(false));
}
OS::Core::Compiler::Expression * OS::Core::Compiler::finishValueExpression(Scope * scope, Expression * exp, const Params& _p)
{
bool is_finished;
Params p = Params(_p)
.setAllowRootBlocks(false);
bool next_allow_auto_call = false;
for(;; p.allow_auto_call = next_allow_auto_call, next_allow_auto_call = false){
if(!recent_token){
return exp;
}
Expression * exp2;
TokenData * token = recent_token;
TokenType token_type = token->type;
switch(token_type){
case Tokenizer::OPERATOR_INDIRECT: // .
// setError(ERROR_SYNTAX, token);
// return NULL;
token = expectToken(Tokenizer::NAME);
if(!token){
allocator->deleteObj(exp);
return NULL;
}
exp2 = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, token);
exp2->ret_values = 1;
OS_ASSERT(scope->function);
exp2->active_locals = scope->function->num_locals;
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_INDIRECT, exp2->token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
readToken();
// p.setAllowCall(true);
next_allow_auto_call = p.allow_auto_call;
continue;
// post ++, post --
case Tokenizer::OPERATOR_INC:
case Tokenizer::OPERATOR_DEC:
if(exp->type != EXP_TYPE_NAME){
return exp;
}
OS_ASSERT(exp->ret_values == 1);
if(!findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, true)){
setError(ERROR_LOCAL_VAL_NOT_DECLARED, exp->token);
allocator->deleteObj(exp);
return NULL;
}
if(scope->function->max_up_count < exp->local_var.up_count){
scope->function->max_up_count = exp->local_var.up_count;
}
exp->type = EXP_TYPE_GET_LOCAL_VAR;
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(token_type == Tokenizer::OPERATOR_INC ? EXP_TYPE_POST_INC : EXP_TYPE_POST_DEC, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
case Tokenizer::OPERATOR_CONCAT: // ..
// case Tokenizer::REST_ARGUMENTS: // ...
case Tokenizer::OPERATOR_LOGIC_AND: // &&
case Tokenizer::OPERATOR_LOGIC_OR: // ||
case Tokenizer::OPERATOR_LOGIC_PTR_EQ: // ===
case Tokenizer::OPERATOR_LOGIC_PTR_NE: // !==
case Tokenizer::OPERATOR_LOGIC_EQ: // ==
case Tokenizer::OPERATOR_LOGIC_NE: // !=
case Tokenizer::OPERATOR_LOGIC_GE: // >=
case Tokenizer::OPERATOR_LOGIC_LE: // <=
case Tokenizer::OPERATOR_LOGIC_GREATER: // >
case Tokenizer::OPERATOR_LOGIC_LESS: // <
case Tokenizer::OPERATOR_LOGIC_NOT: // !
// case Tokenizer::OPERATOR_INC: // ++
// case Tokenizer::OPERATOR_DEC: // --
case Tokenizer::OPERATOR_QUESTION: // ?
// case Tokenizer::OPERATOR_COLON: // :
case Tokenizer::OPERATOR_BIT_AND: // &
case Tokenizer::OPERATOR_BIT_OR: // |
case Tokenizer::OPERATOR_BIT_XOR: // ^
case Tokenizer::OPERATOR_BIT_NOT: // ~
case Tokenizer::OPERATOR_ADD: // +
case Tokenizer::OPERATOR_SUB: // -
case Tokenizer::OPERATOR_MUL: // *
case Tokenizer::OPERATOR_DIV: // /
case Tokenizer::OPERATOR_MOD: // %
case Tokenizer::OPERATOR_LSHIFT: // <<
case Tokenizer::OPERATOR_RSHIFT: // >>
case Tokenizer::OPERATOR_POW: // **
if(!p.allow_binary_operator){
return exp;
}
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, p, is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
continue;
case Tokenizer::PARAM_SEPARATOR:
if(!p.allow_params){
return exp;
}
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, p, is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
continue;
case Tokenizer::OPERATOR_BIT_AND_ASSIGN: // &=
case Tokenizer::OPERATOR_BIT_OR_ASSIGN: // |=
case Tokenizer::OPERATOR_BIT_XOR_ASSIGN: // ^=
case Tokenizer::OPERATOR_BIT_NOT_ASSIGN: // ~=
case Tokenizer::OPERATOR_ADD_ASSIGN: // +=
case Tokenizer::OPERATOR_SUB_ASSIGN: // -=
case Tokenizer::OPERATOR_MUL_ASSIGN: // *=
case Tokenizer::OPERATOR_DIV_ASSIGN: // /=
case Tokenizer::OPERATOR_MOD_ASSIGN: // %=
case Tokenizer::OPERATOR_LSHIFT_ASSIGN: // <<=
case Tokenizer::OPERATOR_RSHIFT_ASSIGN: // >>=
case Tokenizer::OPERATOR_POW_ASSIGN: // **=
setError(ERROR_SYNTAX, token);
return NULL;
case Tokenizer::OPERATOR_ASSIGN: // =
if(!p.allow_assing){ // allow_binary_operator){
return exp;
}
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, Params(p).setAllowAssign(false), is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
return exp;
case Tokenizer::END_ARRAY_BLOCK:
case Tokenizer::END_BRACKET_BLOCK:
case Tokenizer::END_CODE_BLOCK:
case Tokenizer::CODE_SEPARATOR:
return exp;
case Tokenizer::BEGIN_CODE_BLOCK: // {
/* if(!p.allow_auto_call){
return exp;
} */
exp2 = expectObjectExpression(scope, p, false);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
OS_ASSERT(exp2->ret_values == 1);
exp2 = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, exp2->token, exp2 OS_DBG_FILEPOS);
exp2->ret_values = 1;
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL_AUTO_PARAM, token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
// allow_auto_call = false;
continue;
case Tokenizer::NAME:
if(token->str == allocator->core->strings->syntax_in){
if(!p.allow_in_operator || !p.allow_binary_operator){
return exp;
}
token->type = Tokenizer::OPERATOR_IN;
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, p, is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
continue;
}
if(token->str == allocator->core->strings->syntax_isprototypeof){
if(!p.allow_binary_operator){
return exp;
}
token->type = Tokenizer::OPERATOR_ISPROTOTYPEOF;
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, p, is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
continue;
}
if(token->str == allocator->core->strings->syntax_is){
if(!p.allow_binary_operator){
return exp;
}
token->type = Tokenizer::OPERATOR_IS;
exp = finishBinaryOperator(scope, OP_LEVEL_NOTHING, exp, p, is_finished);
if(!exp){
return NULL;
}
OS_ASSERT(is_finished);
continue;
}
// no break
default:
if(!p.allow_auto_call){
return exp;
}
if(token->type == Tokenizer::NAME){
if(token->str == allocator->core->strings->syntax_var
// || token->str == allocator->core->strings->syntax_function
|| token->str == allocator->core->strings->syntax_return
|| token->str == allocator->core->strings->syntax_if
|| token->str == allocator->core->strings->syntax_else
|| token->str == allocator->core->strings->syntax_elseif
|| token->str == allocator->core->strings->syntax_for
|| token->str == allocator->core->strings->syntax_break
|| token->str == allocator->core->strings->syntax_continue
|| token->str == allocator->core->strings->syntax_in
|| token->str == allocator->core->strings->syntax_class
|| token->str == allocator->core->strings->syntax_enum
|| token->str == allocator->core->strings->syntax_switch
|| token->str == allocator->core->strings->syntax_case
|| token->str == allocator->core->strings->syntax_default
|| token->str == allocator->core->strings->syntax_debugger
// || token->str == allocator->core->strings->syntax_debuglocals
)
{
return exp;
}
}
exp2 = expectSingleExpression(scope, Params(p)
.setAllowAssign(false)
.setAllowAutoCall(false)
.setAllowParams(false)
.setAllowRootBlocks(false)); // allow_binary_operator, false, false, false, false);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
OS_ASSERT(exp2->ret_values == 1);
exp2 = expectExpressionValues(exp2, 1);
exp2 = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_PARAMS, exp2->token, exp2 OS_DBG_FILEPOS);
exp2->ret_values = 1;
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL_AUTO_PARAM, token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
// allow_auto_call = false;
continue;
case Tokenizer::BEGIN_BRACKET_BLOCK: // (
if(!p.allow_call){
return exp;
}
exp2 = expectParamsExpression(scope);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL, token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
continue;
case Tokenizer::BEGIN_ARRAY_BLOCK: // [
exp2 = expectParamsExpression(scope);
if(!exp2){
allocator->deleteObj(exp);
return NULL;
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CALL_DIM, token, exp, exp2 OS_DBG_FILEPOS);
exp->ret_values = 1;
if(0 && !p.allow_binary_operator){
return exp;
}
continue;
}
}
return NULL; // shut up compiler
}
OS::Core::Compiler::Params::Params()
{
OS_MEMSET(this, 0, sizeof(*this));
allow_call = true;
}
OS::Core::Compiler::Params::Params(const Params& p)
{
OS_MEMCPY(this, &p, sizeof(p));
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowRootBlocks(bool val)
{
allow_root_blocks = val;
allow_var_decl = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowVarDecl(bool val)
{
allow_var_decl = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowInlineNestedBlock(bool val)
{
allow_inline_nested_block = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowBinaryOperator(bool val)
{
allow_binary_operator = val;
allow_in_operator = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowInOperator(bool val)
{
allow_in_operator = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowAssign(bool val)
{
allow_assing = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowParams(bool val)
{
allow_params = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowAutoCall(bool val)
{
allow_auto_call = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowCall(bool val)
{
allow_call = val;
return *this;
}
OS::Core::Compiler::Params& OS::Core::Compiler::Params::setAllowNopResult(bool val)
{
allow_nop_result = val;
return *this;
}
bool OS::Core::Compiler::isVarNameValid(const String& name)
{
const String * list[] = {
&allocator->core->strings->syntax_super,
&allocator->core->strings->syntax_is,
&allocator->core->strings->syntax_isprototypeof,
&allocator->core->strings->syntax_typeof,
&allocator->core->strings->syntax_valueof,
&allocator->core->strings->syntax_booleanof,
&allocator->core->strings->syntax_numberof,
&allocator->core->strings->syntax_stringof,
&allocator->core->strings->syntax_arrayof,
&allocator->core->strings->syntax_objectof,
&allocator->core->strings->syntax_userdataof,
&allocator->core->strings->syntax_functionof,
&allocator->core->strings->syntax_extends,
&allocator->core->strings->syntax_clone,
&allocator->core->strings->syntax_delete,
&allocator->core->strings->syntax_prototype,
&allocator->core->strings->syntax_var,
&allocator->core->strings->syntax_this,
&allocator->core->strings->syntax_arguments,
&allocator->core->strings->syntax_function,
&allocator->core->strings->syntax_null,
&allocator->core->strings->syntax_true,
&allocator->core->strings->syntax_false,
&allocator->core->strings->syntax_return,
&allocator->core->strings->syntax_class,
&allocator->core->strings->syntax_enum,
&allocator->core->strings->syntax_switch,
&allocator->core->strings->syntax_case,
&allocator->core->strings->syntax_default,
&allocator->core->strings->syntax_if,
&allocator->core->strings->syntax_else,
&allocator->core->strings->syntax_elseif,
&allocator->core->strings->syntax_for,
&allocator->core->strings->syntax_in,
&allocator->core->strings->syntax_break,
&allocator->core->strings->syntax_continue,
&allocator->core->strings->syntax_debugger,
&allocator->core->strings->syntax_debuglocals
};
for(int i = 0; i < (int)(sizeof(list)/sizeof(list[0])); i++){
if(name == *(list[i])){
return false;
}
}
return true;
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectSingleExpression(Scope * scope, bool allow_nop_result, bool allow_inline_nested_block)
{
return expectSingleExpression(scope, Params()
.setAllowAssign(true)
.setAllowAutoCall(true)
.setAllowBinaryOperator(true)
.setAllowParams(true)
.setAllowRootBlocks(true)
.setAllowNopResult(allow_nop_result)
.setAllowInlineNestedBlock(allow_inline_nested_block));
}
OS::Core::Compiler::Expression * OS::Core::Compiler::expectSingleExpression(Scope * scope, const Params& p)
{
#ifdef OS_DEBUG
allocator->checkNativeStackUsage(OS_TEXT("OS::Core::Compiler::expectSingleExpression"));
#endif
TokenData * token = recent_token; // readToken();
if(!token){
setError(ERROR_EXPECT_EXPRESSION, token);
return NULL;
}
Expression * exp;
TokenType token_type = token->type;
switch(token_type){
// begin unary operators
case Tokenizer::OPERATOR_ADD:
case Tokenizer::OPERATOR_SUB:
case Tokenizer::OPERATOR_LENGTH:
case Tokenizer::OPERATOR_BIT_NOT:
case Tokenizer::OPERATOR_LOGIC_NOT:
if(!expectToken()){
return NULL;
}
exp = expectSingleExpression(scope, Params());
if(!exp){
return NULL;
}
OS_ASSERT(exp->ret_values == 1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(getUnaryExpressionType(token_type), exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
return finishValueExpressionNoAutoCall(scope, exp, p); // allow_binary_operator, allow_param, allow_assign, false);
// pre ++, pre --
case Tokenizer::OPERATOR_INC:
case Tokenizer::OPERATOR_DEC:
if(!expectToken(Tokenizer::NAME)){
return NULL;
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_LOCAL_VAR, recent_token);
exp->ret_values = 1;
exp->active_locals = scope->function->num_locals;
if(!findLocalVar(exp->local_var, scope, exp->token->str, exp->active_locals, true)){
setError(ERROR_LOCAL_VAL_NOT_DECLARED, exp->token);
allocator->deleteObj(exp);
return NULL;
}
if(scope->function->max_up_count < exp->local_var.up_count){
scope->function->max_up_count = exp->local_var.up_count;
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(token_type == Tokenizer::OPERATOR_INC ? EXP_TYPE_PRE_INC : EXP_TYPE_PRE_DEC, exp->token, exp OS_DBG_FILEPOS);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
// end unary operators
case Tokenizer::BEGIN_CODE_BLOCK:
if(p.allow_root_blocks){
if(!p.allow_inline_nested_block){
TokenData * check_token = getPrevToken();
if(!check_token || (check_token->type != Tokenizer::CODE_SEPARATOR && check_token->type != Tokenizer::BEGIN_CODE_BLOCK)){
// setError(Tokenizer::CODE_SEPARATOR, recent_token);
setError(ERROR_EXPECT_CODE_SEP_BEFORE_NESTED_BLOCK, recent_token);
return NULL;
}
}
return expectCodeExpression(scope);
}
return expectObjectExpression(scope, p);
case Tokenizer::BEGIN_ARRAY_BLOCK:
return expectArrayExpression(scope, p);
case Tokenizer::BEGIN_BRACKET_BLOCK:
return expectBracketExpression(scope, p);
case Tokenizer::STRING:
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_STRING, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoNextCall(scope, exp, p);
case Tokenizer::NUMBER:
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NUMBER, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoNextCall(scope, exp, p);
case Tokenizer::REST_ARGUMENTS:
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_REST_ARGUMENTS, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoNextCall(scope, exp, p);
case Tokenizer::CODE_SEPARATOR:
case Tokenizer::END_ARRAY_BLOCK:
case Tokenizer::END_BRACKET_BLOCK:
case Tokenizer::END_CODE_BLOCK:
if(!p.allow_nop_result){
return NULL;
}
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NOP, token);
case Tokenizer::NAME:
if(token->str == allocator->core->strings->syntax_var){
if(!p.allow_var_decl){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
return expectVarExpression(scope);
}
if(token->str == allocator->core->strings->syntax_function){
exp = expectFunctionExpression(scope);
if(!exp){
return NULL;
}
if(!exp->ret_values){
return exp;
}
return finishValueExpression(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_return){
return expectReturnExpression(scope);
}
if(token->str == allocator->core->strings->syntax_if){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
return expectIfExpression(scope);
}
if(token->str == allocator->core->strings->syntax_else){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_elseif){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_for){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
return expectForExpression(scope);
}
if(token->str == allocator->core->strings->syntax_in){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_this){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_THIS, token);
exp->ret_values = 1;
readToken();
return finishValueExpression(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_arguments){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_GET_ARGUMENTS, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_null){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_NULL, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_true){
token->setFloat(1);
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_TRUE, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_false){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONST_FALSE, token);
exp->ret_values = 1;
readToken();
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_extends){
exp = expectExtendsExpression(scope);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_clone){
exp = expectCloneExpression(scope);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_delete){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
return expectDeleteExpression(scope);
}
if(token->str == allocator->core->strings->syntax_typeof){
exp = expectValueOfExpression(scope, EXP_TYPE_TYPE_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_valueof){
exp = expectValueOfExpression(scope, EXP_TYPE_VALUE_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_booleanof){
exp = expectValueOfExpression(scope, EXP_TYPE_LOGIC_BOOL);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_numberof){
exp = expectValueOfExpression(scope, EXP_TYPE_NUMBER_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_stringof){
exp = expectValueOfExpression(scope, EXP_TYPE_STRING_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_arrayof){
exp = expectValueOfExpression(scope, EXP_TYPE_ARRAY_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_objectof){
exp = expectValueOfExpression(scope, EXP_TYPE_OBJECT_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_userdataof){
exp = expectValueOfExpression(scope, EXP_TYPE_USERDATA_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_functionof){
exp = expectValueOfExpression(scope, EXP_TYPE_FUNCTION_OF);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_break){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
readToken();
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_BREAK, token);
}
if(token->str == allocator->core->strings->syntax_continue){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
readToken();
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_CONTINUE, token);
}
if(token->str == allocator->core->strings->syntax_debugger){
if(!p.allow_root_blocks){
setError(ERROR_NESTED_ROOT_BLOCK, token);
return NULL;
}
readToken();
return new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_DEBUGGER, token);
}
if(token->str == allocator->core->strings->syntax_debuglocals){
exp = expectDebugLocalsExpression(scope);
if(!exp){
return NULL;
}
return finishValueExpressionNoAutoCall(scope, exp, p);
}
if(token->str == allocator->core->strings->syntax_is){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_isprototypeof){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_class){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_enum){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_switch){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_case || token->str == allocator->core->strings->syntax_default){
setError(ERROR_SYNTAX, token);
return NULL;
}
if(token->str == allocator->core->strings->syntax_super){
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_SUPER, token);
exp->ret_values = 1;
readToken();
return finishValueExpression(scope, exp, p);
}
exp = new (malloc(sizeof(Expression) OS_DBG_FILEPOS)) Expression(EXP_TYPE_NAME, token);
exp->ret_values = 1;
OS_ASSERT(scope->function);
exp->active_locals = scope->function->num_locals;
readToken();
return finishValueExpression(scope, exp, p);
}
setError(ERROR_EXPECT_EXPRESSION, token);
return NULL;
}
void OS::Core::Compiler::debugPrintSourceLine(StringBuffer& out, TokenData * token)
{
if(!token){
return;
}
bool filePrinted = false;
if(recent_printed_text_data != token->text_data){
if(recent_printed_text_data){
recent_printed_text_data->release();
}
filePrinted = true;
recent_printed_line = -1;
recent_printed_text_data = token->text_data->retain();
out += String::format(allocator, OS_TEXT("\n[FILE] %s"), token->text_data->filename.toChar());
}
if(recent_printed_line != token->line && token->line >= 0){
recent_printed_line = token->line;
String line = allocator->core->newStringValue(token->text_data->lines[token->line], true, true);
out += String::format(allocator, OS_TEXT("\n[%d] %s\n\n"), token->line+1, line.toChar());
}
else if(filePrinted){
out += String::format(allocator, OS_TEXT("\n"));
}
return;
}
const OS_CHAR * OS::Core::Compiler::getExpName(ExpressionType type)
{
switch(type){
case EXP_TYPE_NOP:
return OS_TEXT("nop");
case EXP_TYPE_CODE_LIST:
return OS_TEXT("code list");
case EXP_TYPE_CONST_NUMBER:
return OS_TEXT("const number");
case EXP_TYPE_CONST_STRING:
return OS_TEXT("const string");
case EXP_TYPE_CONST_NULL:
return OS_TEXT("const null");
case EXP_TYPE_CONST_TRUE:
return OS_TEXT("const true");
case EXP_TYPE_CONST_FALSE:
return OS_TEXT("const false");
case EXP_TYPE_NAME:
return OS_TEXT("name");
case EXP_TYPE_PARAMS:
return OS_TEXT("params");
case EXP_TYPE_INDIRECT:
return OS_TEXT("indirect");
case EXP_TYPE_SET_PROPERTY:
return OS_TEXT("set property");
case EXP_TYPE_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
return OS_TEXT("set property by locals auto create");
case EXP_TYPE_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
return OS_TEXT("get & set property by locals auto create");
case EXP_TYPE_GET_PROPERTY:
return OS_TEXT("get property");
case EXP_TYPE_GET_THIS_PROPERTY_BY_STRING:
return OS_TEXT("get this property by string");
case EXP_TYPE_GET_PROPERTY_BY_LOCALS:
return OS_TEXT("get property by locals");
case EXP_TYPE_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
return OS_TEXT("get property by local & number");
case EXP_TYPE_GET_PROPERTY_AUTO_CREATE:
return OS_TEXT("get property auto create");
case EXP_TYPE_SET_DIM:
return OS_TEXT("set dim");
case EXP_TYPE_POP_VALUE:
return OS_TEXT("pop");
case EXP_TYPE_CALL:
case EXP_TYPE_CALL_AUTO_PARAM:
return OS_TEXT("call");
case EXP_TYPE_CALL_DIM:
return OS_TEXT("dim");
// case EXP_TYPE_GET_DIM:
// return OS_TEXT("get dim");
case EXP_TYPE_CALL_METHOD:
return OS_TEXT("call method");
case EXP_TYPE_TAIL_CALL_METHOD:
return OS_TEXT("tail call method");
case EXP_TYPE_TAIL_CALL:
return OS_TEXT("tail call");
case EXP_TYPE_VALUE:
return OS_TEXT("single value");
case EXP_TYPE_EXTENDS:
return OS_TEXT("extends");
case EXP_TYPE_CLONE:
return OS_TEXT("clone");
case EXP_TYPE_DELETE:
return OS_TEXT("delete");
case EXP_TYPE_RETURN:
return OS_TEXT("return");
case EXP_TYPE_FUNCTION:
return OS_TEXT("function");
case EXP_TYPE_SCOPE:
return OS_TEXT("scope");
case EXP_TYPE_LOOP_SCOPE:
return OS_TEXT("loop");
case EXP_TYPE_GET_THIS:
return OS_TEXT("push this");
case EXP_TYPE_GET_ARGUMENTS:
return OS_TEXT("push arguments");
case EXP_TYPE_GET_REST_ARGUMENTS:
return OS_TEXT("push rest arguments");
case EXP_TYPE_GET_LOCAL_VAR:
return OS_TEXT("get local var");
case EXP_TYPE_GET_LOCAL_VAR_AUTO_CREATE:
return OS_TEXT("get local var auto create");
case EXP_TYPE_GET_ENV_VAR:
return OS_TEXT("get env var");
case EXP_TYPE_GET_ENV_VAR_AUTO_CREATE:
return OS_TEXT("get env var auto create");
case EXP_TYPE_SET_LOCAL_VAR:
return OS_TEXT("set local var");
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS:
return OS_TEXT("set local var by bin operator locals");
case EXP_TYPE_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER:
return OS_TEXT("set local var by bin operator local & number");
case EXP_TYPE_SET_ENV_VAR:
return OS_TEXT("set env var");
case EXP_TYPE_BIN_OPERATOR_BY_LOCALS:
return OS_TEXT("binary operator by locals");
case EXP_TYPE_BIN_OPERATOR_BY_LOCAL_AND_NUMBER:
return OS_TEXT("binary operator by local & number");
case EXP_TYPE_ASSIGN:
return OS_TEXT("operator =");
case EXP_TYPE_LOGIC_AND: // &&
return OS_TEXT("logic &&");
case EXP_TYPE_LOGIC_OR: // ||
return OS_TEXT("logic ||");
case EXP_TYPE_LOGIC_PTR_EQ: // ===
return OS_TEXT("logic ===");
case EXP_TYPE_LOGIC_PTR_NE: // !==
return OS_TEXT("logic !==");
case EXP_TYPE_LOGIC_EQ: // ==
return OS_TEXT("logic ==");
case EXP_TYPE_LOGIC_NE: // !=
return OS_TEXT("logic !=");
case EXP_TYPE_LOGIC_GE: // >=
return OS_TEXT("logic >=");
case EXP_TYPE_LOGIC_LE: // <=
return OS_TEXT("logic <=");
case EXP_TYPE_LOGIC_GREATER: // >
return OS_TEXT("logic >");
case EXP_TYPE_LOGIC_LESS: // <
return OS_TEXT("logic <");
case EXP_TYPE_LOGIC_BOOL: // !!
return OS_TEXT("logic bool");
case EXP_TYPE_LOGIC_NOT: // !
return OS_TEXT("logic not");
case EXP_TYPE_PLUS:
return OS_TEXT("plus");
case EXP_TYPE_NEG:
return OS_TEXT("neg");
case EXP_TYPE_LENGTH:
return OS_TEXT("length");
case EXP_TYPE_IN:
return OS_TEXT("in");
case EXP_TYPE_ISPROTOTYPEOF:
return OS_TEXT("isprototypeof");
case EXP_TYPE_IS:
return OS_TEXT("is");
case EXP_TYPE_SUPER:
return OS_TEXT("super");
case EXP_TYPE_TYPE_OF:
return OS_TEXT("typeof");
case EXP_TYPE_VALUE_OF:
return OS_TEXT("valueof");
case EXP_TYPE_NUMBER_OF:
return OS_TEXT("numberof");
case EXP_TYPE_STRING_OF:
return OS_TEXT("stringof");
case EXP_TYPE_ARRAY_OF:
return OS_TEXT("arrayof");
case EXP_TYPE_OBJECT_OF:
return OS_TEXT("objectof");
case EXP_TYPE_USERDATA_OF:
return OS_TEXT("userdataof");
case EXP_TYPE_FUNCTION_OF:
return OS_TEXT("functionof");
/*
case EXP_TYPE_INC: // ++
return OS_TEXT("operator ++");
case EXP_TYPE_DEC: // --
return OS_TEXT("operator --");
*/
case EXP_TYPE_PRE_INC: // ++
return OS_TEXT("pre ++");
case EXP_TYPE_PRE_DEC: // --
return OS_TEXT("pre --");
case EXP_TYPE_POST_INC: // ++
return OS_TEXT("post ++");
case EXP_TYPE_POST_DEC: // --
return OS_TEXT("post --");
case EXP_TYPE_BIT_AND: // &
return OS_TEXT("bit &");
case EXP_TYPE_BIT_OR: // |
return OS_TEXT("bit |");
case EXP_TYPE_BIT_XOR: // ^
return OS_TEXT("bit ^");
case EXP_TYPE_BIT_NOT: // ~
return OS_TEXT("bit ~");
case EXP_TYPE_BIT_AND_ASSIGN: // &=
return OS_TEXT("bit &=");
case EXP_TYPE_BIT_OR_ASSIGN: // |=
return OS_TEXT("bit |=");
case EXP_TYPE_BIT_XOR_ASSIGN: // ^=
return OS_TEXT("bit ^=");
case EXP_TYPE_BIT_NOT_ASSIGN: // ~=
return OS_TEXT("bit ~=");
case EXP_TYPE_CONCAT: // ..
return OS_TEXT("operator ..");
case EXP_TYPE_ADD: // +
return OS_TEXT("operator +");
case EXP_TYPE_SUB: // -
return OS_TEXT("operator -");
case EXP_TYPE_MUL: // *
return OS_TEXT("operator *");
case EXP_TYPE_DIV: // /
return OS_TEXT("operator /");
case EXP_TYPE_MOD: // %
return OS_TEXT("operator %");
case EXP_TYPE_LSHIFT: // <<
return OS_TEXT("operator <<");
case EXP_TYPE_RSHIFT: // >>
return OS_TEXT("operator >>");
case EXP_TYPE_POW: // **
return OS_TEXT("operator **");
case EXP_TYPE_ADD_ASSIGN: // +=
return OS_TEXT("operator +=");
case EXP_TYPE_SUB_ASSIGN: // -=
return OS_TEXT("operator -=");
case EXP_TYPE_MUL_ASSIGN: // *=
return OS_TEXT("operator *=");
case EXP_TYPE_DIV_ASSIGN: // /=
return OS_TEXT("operator /=");
case EXP_TYPE_MOD_ASSIGN: // %=
return OS_TEXT("operator %=");
case EXP_TYPE_LSHIFT_ASSIGN: // <<=
return OS_TEXT("operator <<=");
case EXP_TYPE_RSHIFT_ASSIGN: // >>=
return OS_TEXT("operator >>=");
case EXP_TYPE_POW_ASSIGN: // **=
return OS_TEXT("operator **=");
}
return OS_TEXT("unknown exp");
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::FunctionDecl::LocalVar::LocalVar(const String& p_name): name(p_name)
{
start_code_pos = -1;
end_code_pos = -1;
}
OS::Core::FunctionDecl::LocalVar::~LocalVar()
{
}
OS::Core::FunctionDecl::FunctionDecl() // Program * p_prog)
{
// prog = p_prog;
#ifdef OS_DEBUG
prog_func_index = -1;
#endif
prog_parent_func_index = -1;
locals = NULL;
num_locals = 0;
num_params = 0;
max_up_count = 0;
func_depth = 0;
func_index = 0;
num_local_funcs = 0;
opcodes_pos = 0;
opcodes_size = 0;
}
OS::Core::FunctionDecl::~FunctionDecl()
{
OS_ASSERT(!locals); // && !prog);
}
// =====================================================================
OS::Core::Program::Program(OS * allocator): filename(allocator)
{
this->allocator = allocator;
ref_count = 1;
opcodes = NULL;
const_numbers = NULL;
const_strings = NULL;
num_numbers = 0;
num_strings = 0;
// gc_time = -1;
}
OS::Core::Program::~Program()
{
OS_ASSERT(ref_count == 0);
int i;
// values could be already destroyed by gc or will be destroyed soon
allocator->free(const_numbers);
const_numbers = NULL;
for(i = 0; i < num_strings; i++){
OS_ASSERT(const_strings[i]->external_ref_count > 0);
const_strings[i]->external_ref_count--;
if(const_strings[i]->gc_color == GC_WHITE){
const_strings[i]->gc_color = GC_BLACK;
}
}
allocator->free(const_strings);
const_strings = NULL;
for(i = 0; i < num_functions; i++){
FunctionDecl * func = functions + i;
for(int j = 0; j < func->num_locals; j++){
func->locals[j].~LocalVar();
}
allocator->free(func->locals);
func->locals = NULL;
// func->prog = NULL;
func->~FunctionDecl();
}
allocator->free(functions);
functions = NULL;
allocator->deleteObj(opcodes);
allocator->vectorClear(debug_info);
}
bool OS::Core::Compiler::saveToStream(StreamWriter * writer, StreamWriter * debug_info_writer)
{
writer->writeBytes(OS_COMPILED_HEADER, OS_STRLEN(OS_COMPILED_HEADER));
int i, len = OS_STRLEN(OS_VERSION)+1;
writer->writeByte(len);
writer->writeBytes(OS_VERSION, len);
MemStreamWriter int_stream(allocator);
MemStreamWriter float_stream(allocator);
MemStreamWriter double_stream(allocator);
int int_count = 0, float_count = 0, double_count = 0;
int int_index = 0, float_index = 0, double_index = 0;
for(i = 0; i < prog_numbers.count; i++){
double val = prog_numbers[i];
if(val >= 0 && (double)(int)val == val){
int_count++;
int_stream.writeUVariable(i - int_index); int_index = i;
int_stream.writeUVariable((int)val);
continue;
}
if((double)(float)val == val){
float_count++;
float_stream.writeUVariable(i - float_index); float_index = i;
float_stream.writeFloat((float)val);
continue;
}
double_count++;
double_stream.writeUVariable(i - double_index); double_index = i;
double_stream.writeDouble(val);
}
// writer->writeUVariable(prog_numbers.count);
writer->writeUVariable(int_count);
writer->writeUVariable(float_count);
writer->writeUVariable(double_count);
writer->writeUVariable(prog_strings.count);
writer->writeUVariable(prog_functions.count);
writer->writeUVariable(prog_opcodes->getPos());
writer->writeBytes(int_stream.buffer.buf, int_stream.buffer.count);
writer->writeBytes(float_stream.buffer.buf, float_stream.buffer.count);
writer->writeBytes(double_stream.buffer.buf, double_stream.buffer.count);
for(i = 0; i < prog_strings.count; i++){
const String& str = prog_strings[i];
int data_size = str.getDataSize();
writer->writeUVariable(data_size);
writer->writeBytes(str.toChar(), data_size);
}
for(i = 0; i < prog_functions.count; i++){
Compiler::Scope * func_scope = prog_functions[i];
writer->writeUVariable(func_scope->parent ? func_scope->parent->func_index+1 : 0); // prog_functions.indexOf(func_scope->parent));
writer->writeUVariable(func_scope->num_locals);
writer->writeUVariable(func_scope->num_params);
writer->writeUVariable(func_scope->max_up_count);
writer->writeUVariable(func_scope->func_depth);
writer->writeUVariable(func_scope->func_index);
writer->writeUVariable(func_scope->num_local_funcs);
writer->writeUVariable(func_scope->opcodes_pos);
writer->writeUVariable(func_scope->opcodes_size);
OS_ASSERT(func_scope->locals_compiled.count == func_scope->num_locals);
for(int j = 0; j < func_scope->locals_compiled.count; j++){
Compiler::Scope::LocalVarCompiled& var_scope = func_scope->locals_compiled[j];
OS_ASSERT(var_scope.start_code_pos >= func_scope->opcodes_pos && var_scope.start_code_pos < func_scope->opcodes_pos+func_scope->opcodes_size);
OS_ASSERT(var_scope.end_code_pos > func_scope->opcodes_pos && var_scope.end_code_pos <= func_scope->opcodes_pos+func_scope->opcodes_size);
writer->writeUVariable(var_scope.cached_name_index);
writer->writeUVariable(var_scope.start_code_pos - func_scope->opcodes_pos);
writer->writeUVariable(var_scope.end_code_pos - func_scope->opcodes_pos);
}
}
writer->writeBytes(prog_opcodes->buffer.buf, prog_opcodes->buffer.count);
if(debug_info_writer){
debug_info_writer->writeBytes(OS_DEBUGINFO_HEADER, OS_STRLEN(OS_DEBUGINFO_HEADER));
len = OS_STRLEN(OS_VERSION)+1;
debug_info_writer->writeByte(len);
debug_info_writer->writeBytes(OS_VERSION, len);
debug_info_writer->writeUVariable(prog_debug_strings.count);
debug_info_writer->writeUVariable(prog_num_debug_infos);
for(i = 0; i < prog_debug_strings.count; i++){
const String& str = prog_debug_strings[i];
int data_size = str.getDataSize();
debug_info_writer->writeUVariable(data_size);
debug_info_writer->writeBytes(str.toChar(), data_size);
}
debug_info_writer->writeBytes(prog_debug_info->buffer.buf, prog_debug_info->buffer.count);
}
return true;
}
bool OS::Core::Program::loadFromStream(StreamReader * reader, StreamReader * debuginfo_reader)
{
OS_ASSERT(!opcodes && !const_numbers && !num_numbers
&& !const_strings && !num_strings && !debug_info.count);
int i, len = OS_STRLEN(OS_COMPILED_HEADER);
if(!reader->checkBytes(OS_COMPILED_HEADER, len)){
return false;
}
len = OS_STRLEN(OS_VERSION)+1;
reader->movePos(1);
if(!reader->checkBytes(OS_VERSION, len)){
return false;
}
int int_count = reader->readUVariable();
int float_count = reader->readUVariable();
int double_count = reader->readUVariable();
num_numbers = int_count + float_count + double_count;
num_strings = reader->readUVariable();
num_functions = reader->readUVariable();
int opcodes_size = reader->readUVariable();
const_numbers = (OS_NUMBER*)allocator->malloc(sizeof(OS_NUMBER) * num_numbers OS_DBG_FILEPOS);
const_strings = (GCStringValue**)allocator->malloc(sizeof(GCStringValue*) * num_strings OS_DBG_FILEPOS);
int num_index = 0;
for(i = 0; i < int_count; i++){
num_index += reader->readUVariable();
OS_ASSERT(num_index >= 0 && num_index < num_numbers);
OS_NUMBER number = (OS_NUMBER)reader->readUVariable();
const_numbers[num_index] = number;
}
for(num_index = 0, i = 0; i < float_count; i++){
num_index += reader->readUVariable();
OS_ASSERT(num_index >= 0 && num_index < num_numbers);
OS_NUMBER number = (OS_NUMBER)reader->readFloat();
const_numbers[num_index] = number;
}
for(num_index = 0, i = 0; i < double_count; i++){
num_index += reader->readUVariable();
OS_ASSERT(num_index >= 0 && num_index < num_numbers);
OS_NUMBER number = (OS_NUMBER)reader->readDouble();
const_numbers[num_index] = number;
}
StringBuffer buf(allocator);
for(i = 0; i < num_strings; i++){
int data_size = reader->readUVariable();
allocator->vectorReserveCapacity(buf, data_size/sizeof(OS_CHAR) + 1 OS_DBG_FILEPOS);
reader->readBytes((void*)buf.buf, data_size);
buf.count = data_size/sizeof(OS_CHAR);
const_strings[i] = buf.toGCStringValue();
const_strings[i]->external_ref_count++;
}
functions = (FunctionDecl*)allocator->malloc(sizeof(FunctionDecl) * num_functions OS_DBG_FILEPOS);
for(i = 0; i < num_functions; i++){
FunctionDecl * func = functions + i;
new (func) FunctionDecl();
#ifdef OS_DEBUG
func->prog_func_index = i;
#endif
func->prog_parent_func_index = reader->readUVariable() - 1;
func->num_locals = reader->readUVariable();
func->num_params = reader->readUVariable();
func->max_up_count = reader->readUVariable();
func->func_depth = reader->readUVariable();
func->func_index = reader->readUVariable();
func->num_local_funcs = reader->readUVariable();
func->opcodes_pos = reader->readUVariable();
func->opcodes_size = reader->readUVariable();
func->locals = (FunctionDecl::LocalVar*)allocator->malloc(sizeof(FunctionDecl::LocalVar) * func->num_locals OS_DBG_FILEPOS);
for(int j = 0; j < func->num_locals; j++){
int cached_name_index = reader->readUVariable();
OS_ASSERT(cached_name_index >= 0 && cached_name_index < num_strings);
FunctionDecl::LocalVar * local_var = func->locals + j;
String var_name(const_strings[cached_name_index]);
new (local_var) FunctionDecl::LocalVar(var_name);
local_var->start_code_pos = reader->readUVariable() + func->opcodes_pos;
local_var->end_code_pos = reader->readUVariable() + func->opcodes_pos;
}
}
opcodes = new (allocator->malloc(sizeof(MemStreamReader) OS_DBG_FILEPOS)) MemStreamReader(allocator, opcodes_size);
reader->readBytes(opcodes->buffer, opcodes_size);
if(debuginfo_reader){
len = OS_STRLEN(OS_DEBUGINFO_HEADER);
if(!debuginfo_reader->checkBytes(OS_DEBUGINFO_HEADER, len)){
return false;
}
len = OS_STRLEN(OS_VERSION)+1;
debuginfo_reader->movePos(1);
if(!debuginfo_reader->checkBytes(OS_VERSION, len)){
return false;
}
int num_strings = debuginfo_reader->readUVariable();
int num_debug_infos = debuginfo_reader->readUVariable();
Vector<String> strings;
allocator->vectorReserveCapacity(strings, num_strings OS_DBG_FILEPOS);
StringBuffer buf(allocator);
for(i = 0; i < num_strings; i++){
int data_size = debuginfo_reader->readUVariable();
allocator->vectorReserveCapacity(buf, data_size/sizeof(OS_CHAR) + 1 OS_DBG_FILEPOS);
debuginfo_reader->readBytes((void*)buf.buf, data_size);
buf.count = data_size/sizeof(OS_CHAR);
allocator->vectorAddItem(strings, buf.toString() OS_DBG_FILEPOS);
}
allocator->vectorReserveCapacity(debug_info, num_debug_infos OS_DBG_FILEPOS);
for(i = 0; i < num_debug_infos; i++){
int end_opcode_offs = debuginfo_reader->readUVariable();
int line = debuginfo_reader->readUVariable();
int pos = debuginfo_reader->readUVariable();
int string_index = debuginfo_reader->readUVariable();
allocator->vectorAddItem(debug_info, DebugInfoItem(end_opcode_offs, line, pos, strings[string_index]) OS_DBG_FILEPOS);
}
allocator->vectorClear(strings);
}
return true;
}
OS::Core::Program::DebugInfoItem * OS::Core::Program::getDebugInfo(int opcode_pos)
{
Program::DebugInfoItem * info = NULL;
for(int i = 0; i < debug_info.count; i++){
Program::DebugInfoItem * cur = &debug_info[i];
if(cur->opcode_pos < opcode_pos){
info = cur;
}
if(cur->opcode_pos > opcode_pos){
break;
}
}
return info;
}
OS::Core::Program::DebugInfoItem::DebugInfoItem(int p_opcode_pos, int p_line, int p_pos, const String& p_token): token(p_token)
{
opcode_pos = p_opcode_pos;
line = p_line;
pos = p_pos;
}
void OS::Core::Program::pushStartFunction()
{
int opcode = opcodes->readByte();
if(opcode != OP_PUSH_FUNCTION){
OS_ASSERT(false);
allocator->pushNull();
return;
}
int prog_func_index = opcodes->readUVariable();
OS_ASSERT(prog_func_index == 0); // func_index >= 0 && func_index < num_functions);
FunctionDecl * func_decl = functions + prog_func_index;
OS_ASSERT(func_decl->max_up_count == 0);
GCFunctionValue * func_value = allocator->core->newFunctionValue(NULL, this, func_decl, allocator->core->global_vars);
allocator->core->pushValue(func_value);
if(filename.getDataSize()){
StringBuffer buf(allocator);
buf += OS_TEXT("<<");
buf += allocator->getFilename(filename);
buf += OS_TEXT(">>");
func_value->name = buf.toGCStringValue();
}else{
func_value->name = OS::String(allocator, OS_TEXT("<<CORE>>")).string;
}
allocator->core->gcMarkProgram(this);
OS_ASSERT(func_decl->opcodes_pos == opcodes->getPos());
opcodes->movePos(func_decl->opcodes_size);
}
OS::Core::Program * OS::Core::Program::retain()
{
ref_count++;
return this;
}
void OS::Core::Program::release()
{
if(--ref_count <= 0){
OS_ASSERT(ref_count == 0);
OS * allocator = this->allocator;
this->~Program();
allocator->free(this);
}
}
OS::Core::Program::OpcodeType OS::Core::Program::getOpcodeType(Compiler::ExpressionType exp_type)
{
switch(exp_type){
case Compiler::EXP_TYPE_CALL: return OP_CALL;
case Compiler::EXP_TYPE_CALL_AUTO_PARAM: return OP_CALL;
// case Compiler::EXP_TYPE_GET_DIM: return OP_GET_DIM;
case Compiler::EXP_TYPE_CALL_METHOD: return OP_CALL_METHOD;
case Compiler::EXP_TYPE_TAIL_CALL: return OP_TAIL_CALL;
case Compiler::EXP_TYPE_TAIL_CALL_METHOD: return OP_TAIL_CALL_METHOD;
case Compiler::EXP_TYPE_GET_THIS: return OP_PUSH_THIS;
case Compiler::EXP_TYPE_GET_ARGUMENTS: return OP_PUSH_ARGUMENTS;
case Compiler::EXP_TYPE_GET_REST_ARGUMENTS: return OP_PUSH_REST_ARGUMENTS;
case Compiler::EXP_TYPE_SUPER: return OP_SUPER;
case Compiler::EXP_TYPE_TYPE_OF: return OP_TYPE_OF;
case Compiler::EXP_TYPE_VALUE_OF: return OP_VALUE_OF;
case Compiler::EXP_TYPE_NUMBER_OF: return OP_NUMBER_OF;
case Compiler::EXP_TYPE_STRING_OF: return OP_STRING_OF;
case Compiler::EXP_TYPE_ARRAY_OF: return OP_ARRAY_OF;
case Compiler::EXP_TYPE_OBJECT_OF: return OP_OBJECT_OF;
case Compiler::EXP_TYPE_USERDATA_OF: return OP_USERDATA_OF;
case Compiler::EXP_TYPE_FUNCTION_OF: return OP_FUNCTION_OF;
case Compiler::EXP_TYPE_LOGIC_BOOL: return OP_LOGIC_BOOL;
case Compiler::EXP_TYPE_LOGIC_NOT: return OP_LOGIC_NOT;
case Compiler::EXP_TYPE_BIT_NOT: return OP_BIT_NOT;
case Compiler::EXP_TYPE_PLUS: return OP_PLUS;
case Compiler::EXP_TYPE_NEG: return OP_NEG;
case Compiler::EXP_TYPE_LENGTH: return OP_LENGTH;
case Compiler::EXP_TYPE_CONCAT: return OP_CONCAT;
case Compiler::EXP_TYPE_IN: return OP_IN;
case Compiler::EXP_TYPE_ISPROTOTYPEOF: return OP_ISPROTOTYPEOF;
case Compiler::EXP_TYPE_IS: return OP_IS;
case Compiler::EXP_TYPE_LOGIC_AND: return OP_LOGIC_AND_4;
case Compiler::EXP_TYPE_LOGIC_OR: return OP_LOGIC_OR_4;
case Compiler::EXP_TYPE_LOGIC_PTR_EQ: return OP_LOGIC_PTR_EQ;
case Compiler::EXP_TYPE_LOGIC_PTR_NE: return OP_LOGIC_PTR_NE;
case Compiler::EXP_TYPE_LOGIC_EQ: return OP_LOGIC_EQ;
case Compiler::EXP_TYPE_LOGIC_NE: return OP_LOGIC_NE;
case Compiler::EXP_TYPE_LOGIC_GE: return OP_LOGIC_GE;
case Compiler::EXP_TYPE_LOGIC_LE: return OP_LOGIC_LE;
case Compiler::EXP_TYPE_LOGIC_GREATER: return OP_LOGIC_GREATER;
case Compiler::EXP_TYPE_LOGIC_LESS: return OP_LOGIC_LESS;
case Compiler::EXP_TYPE_BIT_AND: return OP_BIT_AND;
case Compiler::EXP_TYPE_BIT_OR: return OP_BIT_OR;
case Compiler::EXP_TYPE_BIT_XOR: return OP_BIT_XOR;
case Compiler::EXP_TYPE_ADD: return OP_ADD;
case Compiler::EXP_TYPE_SUB: return OP_SUB;
case Compiler::EXP_TYPE_MUL: return OP_MUL;
case Compiler::EXP_TYPE_DIV: return OP_DIV;
case Compiler::EXP_TYPE_MOD: return OP_MOD;
case Compiler::EXP_TYPE_LSHIFT: return OP_LSHIFT;
case Compiler::EXP_TYPE_RSHIFT: return OP_RSHIFT;
case Compiler::EXP_TYPE_POW: return OP_POW;
}
OS_ASSERT(false);
return OP_UNKNOWN;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::StreamWriter::StreamWriter(OS * p_allocator)
{
allocator = p_allocator;
}
OS::Core::StreamWriter::~StreamWriter()
{
}
void OS::Core::StreamWriter::writeFromStream(StreamReader * reader)
{
int size = reader->getSize() - reader->getPos();
int buf_size = 1024 * 16;
void * buf = allocator->malloc(buf_size < size ? buf_size : size OS_DBG_FILEPOS);
OS_ASSERT(buf || !size);
for(; size > 0; size -= buf_size){
int chunk_size = buf_size <= size ? buf_size : size;
reader->readBytes(buf, chunk_size);
writeBytes(buf, chunk_size);
}
allocator->free(buf);
}
void OS::Core::StreamWriter::writeByte(int value)
{
OS_ASSERT(value >= 0 && value <= 0xff);
OS_BYTE le_value = toLittleEndianByteOrder((OS_BYTE)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeByteAtPos(int value, int pos)
{
OS_ASSERT(value >= 0 && value <= 0xff);
OS_BYTE le_value = toLittleEndianByteOrder((OS_BYTE)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeUVariable(int value)
{
OS_ASSERT(value >= 0);
for(;;){
if(value >= 0x7f){
writeByte((value & 0x7f) | 0x80);
value >>= 7;
}else{
writeByte(value);
return;
}
}
}
void OS::Core::StreamWriter::writeU16(int value)
{
OS_ASSERT(value >= 0 && value <= 0xffff);
OS_U16 le_value = toLittleEndianByteOrder((OS_U16)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeU16AtPos(int value, int pos)
{
OS_ASSERT(value >= 0 && value <= 0xffff);
OS_U16 le_value = toLittleEndianByteOrder((OS_U16)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeInt8(int value)
{
OS_ASSERT((int)(OS_INT8)value == value);
OS_INT8 le_value = toLittleEndianByteOrder((OS_INT8)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeInt8AtPos(int value, int pos)
{
OS_ASSERT((int)(OS_INT8)value == value);
OS_INT8 le_value = toLittleEndianByteOrder((OS_INT8)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeInt16(int value)
{
OS_ASSERT((int)(OS_INT16)value == value);
OS_INT16 le_value = toLittleEndianByteOrder((OS_INT16)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeInt16AtPos(int value, int pos)
{
OS_ASSERT((int)(OS_INT16)value == value);
OS_INT16 le_value = toLittleEndianByteOrder((OS_INT16)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeInt32(int value)
{
OS_INT32 le_value = toLittleEndianByteOrder((OS_INT32)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeInt32AtPos(int value, int pos)
{
OS_ASSERT((int)(OS_INT32)value == value);
OS_INT32 le_value = toLittleEndianByteOrder((OS_INT32)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeInt64(OS_INT64 value)
{
OS_INT64 le_value = toLittleEndianByteOrder((OS_INT64)value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeInt64AtPos(OS_INT64 value, int pos)
{
OS_INT64 le_value = toLittleEndianByteOrder((OS_INT64)value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeFloat(float value)
{
float le_value = toLittleEndianByteOrder(value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeFloatAtPos(float value, int pos)
{
float le_value = toLittleEndianByteOrder(value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
void OS::Core::StreamWriter::writeDouble(double value)
{
double le_value = toLittleEndianByteOrder(value);
writeBytes(&le_value, sizeof(le_value));
}
void OS::Core::StreamWriter::writeDoubleAtPos(double value, int pos)
{
double le_value = toLittleEndianByteOrder(value);
writeBytesAtPos(&le_value, sizeof(le_value), pos);
}
// =====================================================================
OS::Core::MemStreamWriter::MemStreamWriter(OS * allocator): StreamWriter(allocator)
{
pos = 0;
}
OS::Core::MemStreamWriter::~MemStreamWriter()
{
allocator->vectorClear(buffer);
}
int OS::Core::MemStreamWriter::getPos() const
{
return pos;
}
void OS::Core::MemStreamWriter::setPos(int new_pos)
{
OS_ASSERT(new_pos >= 0 && new_pos <= buffer.count);
pos = new_pos;
}
int OS::Core::MemStreamWriter::getSize() const
{
return buffer.count;
}
void OS::Core::MemStreamWriter::writeBytes(const void * buf, int len)
{
// int pos = buffer.count;
allocator->vectorReserveCapacity(buffer, pos + len OS_DBG_FILEPOS);
int save_pos = pos;
pos += len;
if(buffer.count <= pos){
buffer.count = pos;
}
writeBytesAtPos(buf, len, save_pos);
}
void OS::Core::MemStreamWriter::writeBytesAtPos(const void * buf, int len, int pos)
{
OS_ASSERT(pos >= 0 && pos <= buffer.count-len);
OS_MEMCPY(buffer.buf+pos, buf, len);
}
void OS::Core::MemStreamWriter::writeByte(int value)
{
OS_ASSERT(value >= 0 && value <= 0xff);
if(pos < buffer.count){
OS_ASSERT(pos >= 0);
buffer[pos++] = (OS_BYTE)value;
}else{
allocator->vectorAddItem(buffer, (OS_BYTE)value OS_DBG_FILEPOS);
pos++;
}
}
void OS::Core::MemStreamWriter::writeByteAtPos(int value, int pos)
{
OS_ASSERT(value >= 0 && value <= 0xff);
OS_ASSERT(pos >= 0 && pos <= buffer.count-1);
buffer[pos] = (OS_BYTE)value;
}
// =====================================================================
OS::Core::FileStreamWriter::FileStreamWriter(OS * allocator, const OS_CHAR * filename): StreamWriter(allocator)
{
f = allocator->openFile(filename, "wb");
}
OS::Core::FileStreamWriter::~FileStreamWriter()
{
allocator->closeFile(f);
}
int OS::Core::FileStreamWriter::getPos() const
{
return allocator->seekFile(f, 0, SEEK_CUR);
}
void OS::Core::FileStreamWriter::setPos(int new_pos)
{
OS_ASSERT(new_pos >= 0 && new_pos <= getSize());
allocator->seekFile(f, new_pos, SEEK_SET);
}
int OS::Core::FileStreamWriter::getSize() const
{
if(f){
int save_pos = getPos();
allocator->seekFile(f, 0, SEEK_END);
int size = getPos();
allocator->seekFile(f, save_pos, SEEK_SET);
return size;
}
return 0;
}
void OS::Core::FileStreamWriter::writeBytes(const void * buf, int len)
{
allocator->writeFile(buf, len, f);
}
void OS::Core::FileStreamWriter::writeBytesAtPos(const void * buf, int len, int pos)
{
int save_pos = getPos();
allocator->seekFile(f, pos, SEEK_SET);
writeBytes(buf, len);
allocator->seekFile(f, save_pos, SEEK_SET);
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::StreamReader::StreamReader(OS * p_allocator)
{
allocator = p_allocator;
}
OS::Core::StreamReader::~StreamReader()
{
}
OS_BYTE OS::Core::StreamReader::readByte()
{
OS_BYTE le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_BYTE OS::Core::StreamReader::readByteAtPos(int pos)
{
OS_BYTE le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
int OS::Core::StreamReader::readUVariable()
{
int value = readByte();
if(!(value & 0x80)){
return value;
}
value &= 0x7f;
for(int i = 7;; i += 7){
int b = readByte();
if(b & 0x80){
value |= (b & 0x7f) << i;
}else{
OS_ASSERT((value | (b << i)) >= 0);
return value | (b << i);
}
}
return 0; // shut up compiler
}
OS_U16 OS::Core::StreamReader::readU16()
{
OS_U16 le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_U16 OS::Core::StreamReader::readU16AtPos(int pos)
{
OS_U16 le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
OS_INT8 OS::Core::StreamReader::readInt8()
{
OS_INT8 le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_INT8 OS::Core::StreamReader::readInt8AtPos(int pos)
{
OS_INT8 le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
OS_INT16 OS::Core::StreamReader::readInt16()
{
OS_INT16 le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_INT16 OS::Core::StreamReader::readInt16AtPos(int pos)
{
OS_INT16 le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
OS_INT32 OS::Core::StreamReader::readInt32()
{
OS_INT32 le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_INT32 OS::Core::StreamReader::readInt32AtPos(int pos)
{
OS_INT32 le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
OS_INT64 OS::Core::StreamReader::readInt64()
{
OS_INT64 le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
OS_INT64 OS::Core::StreamReader::readInt64AtPos(int pos)
{
OS_INT64 le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
float OS::Core::StreamReader::readFloat()
{
float le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
float OS::Core::StreamReader::readFloatAtPos(int pos)
{
float le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
double OS::Core::StreamReader::readDouble()
{
double le_value;
readBytes(&le_value, sizeof(le_value));
return fromLittleEndianByteOrder(le_value);
}
double OS::Core::StreamReader::readDoubleAtPos(int pos)
{
double le_value;
readBytesAtPos(&le_value, sizeof(le_value), pos);
return fromLittleEndianByteOrder(le_value);
}
// =====================================================================
OS::Core::MemStreamReader::MemStreamReader(OS * allocator, int buf_size): StreamReader(allocator)
{
cur = buffer = (OS_BYTE*)allocator->malloc(buf_size OS_DBG_FILEPOS);
size = buf_size;
}
OS::Core::MemStreamReader::MemStreamReader(OS * allocator, OS_BYTE * buf, int buf_size): StreamReader(allocator)
{
cur = buffer = buf;
size = buf_size;
}
OS::Core::MemStreamReader::~MemStreamReader()
{
if(allocator){
allocator->free(buffer);
}
}
int OS::Core::MemStreamReader::getPos() const
{
return cur - buffer;
}
void OS::Core::MemStreamReader::setPos(int new_pos)
{
OS_ASSERT(new_pos >= 0 && new_pos <= size);
cur = buffer + new_pos;
}
int OS::Core::MemStreamReader::getSize() const
{
return size;
}
void OS::Core::MemStreamReader::movePos(int len)
{
OS_ASSERT(getPos()+len >= 0 && getPos()+len <= size);
cur += len;
}
bool OS::Core::MemStreamReader::checkBytes(const void * src, int len)
{
OS_ASSERT(getPos() >= 0 && getPos()+len <= size);
bool r = OS_MEMCMP(cur, src, len) == 0;
cur += len;
return r;
}
void * OS::Core::MemStreamReader::readBytes(void * dst, int len)
{
OS_ASSERT(getPos() >= 0 && getPos()+len <= size);
OS_MEMCPY(dst, cur, len);
cur += len;
return dst;
}
void * OS::Core::MemStreamReader::readBytesAtPos(void * dst, int len, int pos)
{
OS_ASSERT(pos >= 0 && pos+len <= size);
OS_MEMCPY(dst, buffer + pos, len);
return dst;
}
OS_BYTE OS::Core::MemStreamReader::readByte()
{
OS_ASSERT(getPos() >= 0 && getPos()+(int)sizeof(OS_BYTE) <= size);
return *cur++;
}
OS_BYTE OS::Core::MemStreamReader::readByteAtPos(int pos)
{
OS_ASSERT(pos >= 0 && pos+(int)sizeof(OS_BYTE) <= size);
return buffer[pos];
}
OS_INT8 OS::Core::MemStreamReader::readInt8()
{
OS_ASSERT(getPos() >= 0 && getPos()+1 <= size);
return (OS_INT8)*cur++;
}
OS_INT16 OS::Core::MemStreamReader::readInt16()
{
OS_ASSERT(getPos() >= 0 && getPos()+(int)sizeof(OS_INT16) <= size);
OS_BYTE * buf = cur;
cur += sizeof(OS_INT16);
OS_INT16 value = buf[0] | (buf[1] << 8);
return value;
}
OS_INT32 OS::Core::MemStreamReader::readInt32()
{
OS_ASSERT(getPos() >= 0 && getPos()+(int)sizeof(OS_INT32) <= size);
OS_BYTE * buf = cur;
cur += sizeof(OS_INT32);
OS_INT32 value = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
return value;
}
// =====================================================================
OS::Core::FileStreamReader::FileStreamReader(OS * allocator, const OS_CHAR * filename): StreamReader(allocator)
{
f = allocator->openFile(filename, "rb");
}
OS::Core::FileStreamReader::~FileStreamReader()
{
allocator->closeFile(f);
}
int OS::Core::FileStreamReader::getPos() const
{
return allocator->seekFile(f, 0, SEEK_CUR);
}
void OS::Core::FileStreamReader::setPos(int new_pos)
{
OS_ASSERT(new_pos >= 0 && new_pos <= getSize());
allocator->seekFile(f, new_pos, SEEK_SET);
}
int OS::Core::FileStreamReader::getSize() const
{
if(f){
int save_pos = getPos();
allocator->seekFile(f, 0, SEEK_END);
int size = getPos();
allocator->seekFile(f, save_pos, SEEK_SET);
return size;
}
return 0;
}
void OS::Core::FileStreamReader::movePos(int len)
{
allocator->seekFile(f, len, SEEK_CUR);
}
bool OS::Core::FileStreamReader::checkBytes(const void * src, int len)
{
void * buf = alloca(len);
readBytes(buf, len);
return OS_MEMCMP(buf, src, len) == 0;
}
void * OS::Core::FileStreamReader::readBytes(void * buf, int len)
{
if(!f || !allocator->readFile(buf, len, f)){
OS_MEMSET(buf, 0, len);
}
return buf;
}
void * OS::Core::FileStreamReader::readBytesAtPos(void * buf, int len, int pos)
{
int save_pos = getPos();
allocator->seekFile(f, pos, SEEK_SET);
readBytes(buf, len);
allocator->seekFile(f, save_pos, SEEK_SET);
return buf;
}
// =====================================================================
// =====================================================================
// =====================================================================
static bool isDecString(const OS_CHAR * str, int len)
{
OS_INT val;
const OS_CHAR * end = str + len;
return parseSimpleDec(str, val) && str == end;
}
OS::Core::PropertyIndex::PropertyIndex(const PropertyIndex& p_index): index(p_index.index)
{
}
OS::Core::PropertyIndex::PropertyIndex(Value p_index): index(p_index)
{
convertIndexStringToNumber();
}
OS::Core::PropertyIndex::PropertyIndex(Value p_index, const KeepStringIndex&): index(p_index)
{
OS_ASSERT(index.type != OS_VALUE_TYPE_STRING || PropertyIndex(p_index).index.type == OS_VALUE_TYPE_STRING);
}
OS::Core::PropertyIndex::PropertyIndex(GCStringValue * p_index): index(p_index)
{
convertIndexStringToNumber();
}
OS::Core::PropertyIndex::PropertyIndex(GCStringValue * p_index, const KeepStringIndex&): index(p_index)
{
// OS_ASSERT(index.type != OS_VALUE_TYPE_STRING || PropertyIndex(p_index).index.type == OS_VALUE_TYPE_STRING);
}
OS::Core::PropertyIndex::PropertyIndex(const String& p_index): index(p_index.string)
{
convertIndexStringToNumber();
}
OS::Core::PropertyIndex::PropertyIndex(const String& p_index, const KeepStringIndex&): index(p_index.string)
{
// OS_ASSERT(index.type != OS_VALUE_TYPE_STRING || PropertyIndex(p_index).index.type == OS_VALUE_TYPE_STRING);
}
void OS::Core::PropertyIndex::convertIndexStringToNumber()
{
if(index.type == OS_VALUE_TYPE_STRING){
OS_NUMBER val;
const OS_CHAR * str = index.v.string->toChar();
if(*str >= OS_TEXT('0') && *str <= OS_TEXT('9')){
const OS_CHAR * end = str + index.v.string->getLen();
if(parseSimpleFloat(str, val)){
if(*str == OS_TEXT('.')){
OS_NUMBER m = (OS_NUMBER)0.1f;
for(str++; *str >= OS_TEXT('0') && *str <= OS_TEXT('9'); str++, m *= (OS_NUMBER)0.1f){
val += (OS_NUMBER)(*str - OS_TEXT('0')) * m;
}
}
if(str == end){
index.v.number = val;
index.type = OS_VALUE_TYPE_NUMBER;
// OS_ASSERT((OS_INT)index.v.number == val);
}
}
}
}
}
bool OS::Core::PropertyIndex::isEqual(const PropertyIndex& b) const
{
switch(index.type){
case OS_VALUE_TYPE_NULL:
return b.index.type == OS_VALUE_TYPE_NULL;
// case OS_VALUE_TYPE_BOOL:
// return b.index.type == OS_VALUE_TYPE_BOOL && index.v.boolean == b.index.v.boolean;
case OS_VALUE_TYPE_NUMBER:
return b.index.type == OS_VALUE_TYPE_NUMBER && index.v.number == b.index.v.number;
}
return index.type == b.index.type && index.v.value == b.index.v.value;
}
bool OS::Core::GCStringValue::isEqual(int hash, const void * b, int size) const
{
return this->hash == hash
&& data_size == size
&& OS_MEMCMP(toMemory(), b, size) == 0;
}
bool OS::Core::GCStringValue::isEqual(int hash, const void * buf1, int size1, const void * buf2, int size2) const
{
if(this->hash != hash || data_size != size1 + size2){
return false;
}
const OS_BYTE * src = toBytes();
return OS_MEMCMP(src, buf1, size1) == 0
&& (!size2 || OS_MEMCMP(src + size1, buf2, size2) == 0);
}
bool OS::Core::PropertyIndex::isEqual(int hash, const void * b, int size) const
{
if(index.type == OS_VALUE_TYPE_STRING){
return index.v.string->hash == hash
&& index.v.string->data_size == size
&& OS_MEMCMP(index.v.string->toMemory(), b, size) == 0;
}
return false;
}
bool OS::Core::PropertyIndex::isEqual(int hash, const void * buf1, int size1, const void * buf2, int size2) const
{
if(index.type == OS_VALUE_TYPE_STRING){
int src_size = index.v.string->data_size;
if(index.v.string->hash != hash || src_size != size1 + size2){
return false;
}
const OS_BYTE * src = index.v.string->toBytes();
return Utils::cmp(src, size1, buf1, size1) == 0
&& Utils::cmp(src + size1, size2, buf2, size2) == 0;
}
return false;
}
template <class T> int getNumberHash(T val)
{
return (int)val;
}
template <> int getNumberHash<double>(double val)
{
float t = (float)val;
return *(int*)&t;
}
template <> int getNumberHash<float>(float t)
{
return *(int*)&t;
}
template <> int getNumberHash<int>(int t)
{
return t;
}
int OS::Core::PropertyIndex::getHash() const
{
switch(index.type){
/*
case OS_VALUE_TYPE_NULL:
return 0;
case OS_VALUE_TYPE_BOOL:
return index.v.boolean;
*/
case OS_VALUE_TYPE_NUMBER:
#if 1 // speed optimization
// return getNumberHash(index.v.number);
{
union {
double d;
OS_INT32 p[2];
} u;
u.d = (double)index.v.number; // + 1.0f;
return u.p[0] + u.p[1];
}
#else
/* if(sizeof(index.v.number) > sizeof(float)){
float t = (float)index.v.number;
return *(int*)&t;
} */
// return (int)index.v.number;
OS_ASSERT(sizeof(int) <= sizeof(index.v.number));
if(IS_LITTLE_ENDIAN){
OS_U32 t = ((OS_U32*)((OS_BYTE*)&index.v.number + sizeof(index.v.number)))[-1];
return (t>>24) | (t<<8);
// return ((int*)((OS_BYTE*)&index.v.number + sizeof(index.v.number)))[-1];
}else{
OS_U32 t = *(OS_U32*)&index.v.number;
return (t>>24) | (t<<8);
}
#endif
case OS_VALUE_TYPE_STRING:
return index.v.string->hash;
}
// all other values share same area with index.v.value so just use it as hash
return (ptrdiff_t) index.v.value;
}
// =====================================================================
OS::Core::Property::Property(const PropertyIndex& index): PropertyIndex(index)
{
hash_next = NULL;
prev = NULL;
next = NULL;
}
OS::Core::Property::Property(Value index): PropertyIndex(index)
{
hash_next = NULL;
prev = NULL;
next = NULL;
}
OS::Core::Property::Property(Value index, const KeepStringIndex& keep): PropertyIndex(index, keep)
{
hash_next = NULL;
prev = NULL;
next = NULL;
}
OS::Core::Property::Property(GCStringValue * index): PropertyIndex(index)
{
hash_next = NULL;
prev = NULL;
next = NULL;
}
OS::Core::Property::Property(GCStringValue * index, const KeepStringIndex& keep): PropertyIndex(index, keep)
{
hash_next = NULL;
prev = NULL;
next = NULL;
}
OS::Core::Property::~Property()
{
OS_ASSERT(!hash_next);
OS_ASSERT(!prev);
OS_ASSERT(!next);
}
// =====================================================================
OS::Core::Table::IteratorState::IteratorState()
{
table = NULL;
prop = NULL;
next = NULL;
ascending = true;
}
OS::Core::Table::IteratorState::~IteratorState()
{
OS_ASSERT(!table && !prop && !next);
}
OS::Core::Table::Table()
{
head_mask = 0;
heads = NULL;
next_index = 0;
count = 0;
first = last = NULL;
iterators = NULL;
}
OS::Core::Table::~Table()
{
OS_ASSERT(count == 0 && !first && !last && !iterators);
OS_ASSERT(!heads);
}
bool OS::Core::Table::containsIterator(IteratorState * iter)
{
for(IteratorState * cur = iterators; cur; cur = cur->next){
if(cur == iter){
OS_ASSERT(iter->table == this);
return true;
}
}
OS_ASSERT(iter->table != this);
return false;
}
void OS::Core::Table::addIterator(IteratorState * iter)
{
OS_ASSERT(!containsIterator(iter));
OS_ASSERT(!iter->prop && !iter->table);
iter->table = this;
iter->prop = iter->ascending ? first : last;
iter->next = iterators;
iterators = iter;
}
void OS::Core::Table::removeIterator(IteratorState * iter)
{
OS_ASSERT(containsIterator(iter));
IteratorState * prev = NULL;
for(IteratorState * cur = iterators; cur; prev = cur, cur = cur->next){
if(cur == iter){
if(!prev){
iterators = cur->next;
}else{
prev->next = cur->next;
}
cur->table = NULL;
cur->next = NULL;
cur->prop = NULL;
return;
}
}
OS_ASSERT(false);
}
OS::Core::Table * OS::Core::newTable(OS_DBG_FILEPOS_START_DECL)
{
return new (malloc(sizeof(Table) OS_DBG_FILEPOS_PARAM)) Table();
}
void OS::Core::clearTable(Table * table)
{
OS_ASSERT(table);
Property * prop = table->last, * prev;
table->count = 0;
table->first = NULL;
table->last = NULL;
for(; prop; prop = prev){
prev = prop->prev;
prop->hash_next = NULL;
prop->prev = NULL;
prop->next = NULL;
prop->~Property();
free(prop);
}
while(table->iterators){
table->removeIterator(table->iterators);
}
// OS_ASSERT(table->count == 0 && !table->first && !table->last);
free(table->heads);
table->heads = NULL;
table->head_mask = 0;
table->next_index = 0;
}
void OS::Core::deleteTable(Table * table)
{
OS_ASSERT(table);
clearTable(table);
table->~Table();
free(table);
}
void OS::Core::addTableProperty(Table * table, Property * prop)
{
OS_ASSERT(prop->next == NULL);
OS_ASSERT(!table->get(*prop));
if((table->count>>HASH_GROW_SHIFT) >= table->head_mask){
int new_size = table->heads ? (table->head_mask+1) * 2 : 4;
int alloc_size = sizeof(Property*)*new_size;
Property ** new_heads = (Property**)malloc(alloc_size OS_DBG_FILEPOS);
OS_ASSERT(new_heads);
OS_MEMSET(new_heads, 0, alloc_size);
Property ** old_heads = table->heads;
table->heads = new_heads;
table->head_mask = new_size-1;
for(Property * cur = table->first; cur; cur = cur->next){
int slot = cur->getHash() & table->head_mask;
cur->hash_next = table->heads[slot];
table->heads[slot] = cur;
}
// delete [] old_heads;
free(old_heads);
}
int slot = prop->getHash() & table->head_mask;
prop->hash_next = table->heads[slot];
table->heads[slot] = prop;
if(!table->first){
table->first = prop;
}else{
OS_ASSERT(table->last);
table->last->next = prop;
prop->prev = table->last;
}
table->last = prop;
if(prop->index.type == OS_VALUE_TYPE_NUMBER && table->next_index <= prop->index.v.number){
table->next_index = (OS_INT)prop->index.v.number + 1;
}
table->count++;
}
void OS::Core::changePropertyIndex(Table * table, Property * prop, const PropertyIndex& new_index)
{
int slot = prop->getHash() & table->head_mask;
Property * cur = table->heads[slot], * chain_prev = NULL;
for(; cur; chain_prev = cur, cur = cur->hash_next){
if(cur == prop){ // cur->isEqual(index)){
if(chain_prev){
chain_prev->hash_next = cur->hash_next;
}else{
table->heads[slot] = cur->hash_next;
}
break;
}
}
OS_ASSERT(cur && cur == prop);
if(cur){
*prop = new_index;
slot = prop->getHash() & table->head_mask;
prop->hash_next = table->heads[slot];
table->heads[slot] = prop;
if(prop->index.type == OS_VALUE_TYPE_NUMBER && table->next_index <= prop->index.v.number){
table->next_index = (OS_INT)prop->index.v.number + 1;
}
}
}
OS::Core::Property * OS::Core::removeTableProperty(Table * table, const PropertyIndex& index)
{
OS_ASSERT(table);
int slot = index.getHash() & table->head_mask;
Property * cur = table->heads[slot], * chain_prev = NULL;
for(; cur; chain_prev = cur, cur = cur->hash_next){
if(cur->isEqual(index)){
if(table->first == cur){
table->first = cur->next;
if(table->first){
table->first->prev = NULL;
}
}else{
OS_ASSERT(cur->prev);
cur->prev->next = cur->next;
}
if(table->last == cur){
table->last = cur->prev;
if(table->last){
table->last->next = NULL;
}
}else{
OS_ASSERT(cur->next);
cur->next->prev = cur->prev;
}
if(chain_prev){
chain_prev->hash_next = cur->hash_next;
}else{
table->heads[slot] = cur->hash_next;
}
for(Table::IteratorState * iter = table->iterators; iter; iter = iter->next){
if(iter->prop == cur){
iter->prop = iter->ascending ? cur->next : cur->prev;
}
}
cur->next = NULL;
cur->prev = NULL;
cur->hash_next = NULL;
// cur->value.clear();
table->count--;
return cur;
}
}
return NULL;
}
bool OS::Core::deleteTableProperty(Table * table, const PropertyIndex& index)
{
Property * prop = removeTableProperty(table, index);
if(prop){
prop->~Property();
free(prop);
return true;
}
return false;
}
void OS::Core::deleteValueProperty(GCValue * table_value, const PropertyIndex& index, bool anonymous_del_enabled, bool named_del_enabled, bool prototype_enabled)
{
Table * table = table_value->table;
if(table && deleteTableProperty(table, index)){
return;
}
if(1){ // prototype_enabled){
GCValue * cur_value = table_value;
while(cur_value->prototype){
cur_value = cur_value->prototype;
Table * cur_table = cur_value->table;
if(!cur_table){
continue;
}
if(prototype_enabled){
if(cur_table && deleteTableProperty(cur_table, index)){
return;
}
}else{
if(cur_table && cur_table->get(index)){
return;
}
}
}
}
if(index.index.type == OS_VALUE_TYPE_STRING && strings->syntax_prototype == index.index.v.string){
return;
}
if(table_value->type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<GCArrayValue*>(table_value));
GCArrayValue * arr = (GCArrayValue*)table_value;
int i = (int)valueToInt(index.index);
if(i >= 0 && i < arr->values.count){
allocator->vectorRemoveAtIndex(arr->values, i);
}
return;
}
if((anonymous_del_enabled || named_del_enabled) && !hasSpecialPrefix(index.index)){
Value value;
if(index.index.type == OS_VALUE_TYPE_STRING && named_del_enabled){
const void * buf1 = strings->__delAt.toChar();
int size1 = strings->__delAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * del_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(value, table_value, PropertyIndex(del_name, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& value.isFunction())
{
pushValue(value);
pushValue(table_value);
pushValue(index.index);
call(1, 0);
return;
}
}
if(anonymous_del_enabled && getPropertyValue(value, table_value, PropertyIndex(strings->__del, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& value.isFunction())
{
pushValue(value);
pushValue(table_value);
pushValue(index.index);
call(1, 0);
}
}
}
void OS::Core::deleteValueProperty(Value table_value, const PropertyIndex& index, bool anonymous_del_enabled, bool named_del_enabled, bool prototype_enabled)
{
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
return;
case OS_VALUE_TYPE_BOOL:
if(prototype_enabled){
return deleteValueProperty(prototypes[PROTOTYPE_BOOL], index, anonymous_del_enabled, named_del_enabled, prototype_enabled);
}
return;
case OS_VALUE_TYPE_NUMBER:
if(prototype_enabled){
return deleteValueProperty(prototypes[PROTOTYPE_NUMBER], index, anonymous_del_enabled, named_del_enabled, prototype_enabled);
}
return;
case OS_VALUE_TYPE_STRING:
/* if(prototype_enabled){
return deleteValueProperty(prototypes[PROTOTYPE_STRING], index, anonymous_del_enabled, named_del_enabled, prototype_enabled);
}
return; */
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return deleteValueProperty(table_value.v.value, index, anonymous_del_enabled, named_del_enabled, prototype_enabled);
}
}
void OS::Core::copyTableProperties(Table * dst, Table * src)
{
// OS_ASSERT(dst->count == 0);
for(Property * prop = src->first; prop; prop = prop->next){
setTableValue(dst, PropertyIndex(*prop), prop->value);
}
}
void OS::Core::copyTableProperties(GCValue * dst_value, GCValue * src_value, bool anonymous_setter_enabled, bool named_setter_enabled)
{
if(src_value->table){
for(Property * prop = src_value->table->first; prop; prop = prop->next){
setPropertyValue(dst_value, *prop, prop->value, anonymous_setter_enabled, named_setter_enabled);
}
}
}
void OS::Core::sortTable(Table * table, int(*comp)(OS*, const void*, const void*, void*), void * user_param, bool reorder_keys)
{
if(table->count > 1){
Property ** props = (Property**)malloc(sizeof(Property*) * table->count OS_DBG_FILEPOS);
int i = 0;
Property * cur = table->first;
for(; cur && i < table->count; cur = cur->next, i++){
props[i] = cur;
}
OS_ASSERT(!cur && i == table->count);
allocator->qsort(props, table->count, sizeof(Core::Property*), comp, user_param);
table->first = props[0];
props[0]->prev = NULL;
for(i = 1; i < table->count; i++){
props[i-1]->next = props[i];
props[i]->prev = props[i-1];
}
props[i-1]->next = NULL;
table->last = props[i-1];
if(reorder_keys){
#if 1 // speed optimization
OS_MEMSET(table->heads, 0, sizeof(Property*)*(table->head_mask+1));
for(i = 0; i < table->count; i++){
Property * cur = props[i];
cur->index = Value(i);
int slot = cur->getHash() & table->head_mask;
cur->hash_next = table->heads[slot];
table->heads[slot] = cur;
}
#else
for(i = 0; i < table->count; i++){
changePropertyIndex(table, props[i], Value(i));
}
#endif
table->next_index = table->count;
}
free(props);
}
}
void OS::Core::sortArray(GCArrayValue * arr, int(*comp)(OS*, const void*, const void*, void*), void * user_param)
{
allocator->qsort(arr->values.buf, arr->values.count, sizeof(Value), comp, user_param);
}
static int compareResult(OS_NUMBER num)
{
if(num < 0) return -1;
if(num > 0) return 1;
return 0;
}
int OS::Core::comparePropValues(OS * os, const void * a, const void * b, void*)
{
Property * props[] = {*(Property**)a, *(Property**)b};
os->core->pushOpResultValue(Program::OP_COMPARE, props[0]->value, props[1]->value);
return compareResult(os->popNumber());
}
int OS::Core::comparePropValuesReverse(OS * os, const void * a, const void * b, void * user_param)
{
return comparePropValues(os, b, a, user_param);
}
int OS::Core::compareObjectProperties(OS * os, const void * a, const void * b, void * user_param)
{
Property * props[] = {*(Property**)a, *(Property**)b};
const String& name = *(String*)user_param;
os->core->pushValue(props[0]->value);
os->core->pushStringValue(name);
os->getProperty();
os->core->pushValue(props[1]->value);
os->core->pushStringValue(name);
os->getProperty();
os->runOp(OP_COMPARE);
return compareResult(os->popNumber());
}
int OS::Core::compareObjectPropertiesReverse(OS * os, const void * a, const void * b, void * user_param)
{
return compareObjectProperties(os, b, a, user_param);
}
int OS::Core::compareUserPropValues(OS * os, const void * a, const void * b, void*)
{
Property * props[] = {*(Property**)a, *(Property**)b};
os->pushStackValue(-1);
os->pushNull();
os->core->pushValue(props[0]->value);
os->core->pushValue(props[1]->value);
os->core->pushValue(props[0]->index);
os->core->pushValue(props[1]->index);
os->call(4, 1);
return compareResult(os->popNumber());
}
int OS::Core::compareUserPropValuesReverse(OS * os, const void * a, const void * b, void * user_param)
{
return compareUserPropValues(os, b, a, user_param);
}
int OS::Core::comparePropKeys(OS * os, const void * a, const void * b, void*)
{
Property * props[] = {*(Property**)a, *(Property**)b};
os->core->pushOpResultValue(Program::OP_COMPARE, props[0]->index, props[1]->index);
return compareResult(os->popNumber());
}
int OS::Core::comparePropKeysReverse(OS * os, const void * a, const void * b, void * user_param)
{
return comparePropKeys(os, b, a, user_param);
}
int OS::Core::compareUserPropKeys(OS * os, const void * a, const void * b, void*)
{
Property * props[] = {*(Property**)a, *(Property**)b};
os->pushStackValue(-1);
os->pushNull();
os->core->pushValue(props[0]->index);
os->core->pushValue(props[1]->index);
os->core->pushValue(props[0]->value);
os->core->pushValue(props[1]->value);
os->call(4, 1);
return compareResult(os->popNumber());
}
int OS::Core::compareUserPropKeysReverse(OS * os, const void * a, const void * b, void * user_param)
{
return compareUserPropKeys(os, b, a, user_param);
}
int OS::Core::compareArrayValues(OS * os, const void * a, const void * b, void*)
{
Value * values[] = {(Value*)a, (Value*)b};
os->core->pushOpResultValue(Program::OP_COMPARE, *values[0], *values[1]);
return compareResult(os->popNumber());
}
int OS::Core::compareArrayValuesReverse(OS * os, const void * a, const void * b, void * user_param)
{
return compareArrayValues(os, b, a, user_param);
}
int OS::Core::compareUserArrayValues(OS * os, const void * a, const void * b, void*)
{
Value * values[] = {(Value*)a, (Value*)b};
os->pushStackValue(-1);
os->pushNull();
os->core->pushValue(*values[0]);
os->core->pushValue(*values[1]);
os->call(2, 1);
return compareResult(os->popNumber());
}
int OS::Core::compareUserArrayValuesReverse(OS * os, const void * a, const void * b, void * user_param)
{
return compareUserArrayValues(os, b, a, user_param);
}
int OS::Core::compareUserReverse(OS * os, const void * a, const void * b, void * user_param)
{
int (*comp)(OS*, const void*, const void*, void*) = (int(*)(OS*, const void*, const void*, void*))user_param;
return comp(os, b, a, NULL);
}
OS::Core::Property * OS::Core::Table::get(const PropertyIndex& index)
{
if(heads){
Property * cur = heads[index.getHash() & head_mask];
for(; cur; cur = cur->hash_next){
if(cur->isEqual(index)){
return cur;
}
}
}
return NULL;
}
// =====================================================================
OS::Core::GCFunctionValue::GCFunctionValue()
{
/*
prog = NULL;
func_decl = NULL;
env = NULL;
upvalues = NULL;
*/
}
OS::Core::GCFunctionValue::~GCFunctionValue()
{
OS_ASSERT(!upvalues && !name);
OS_ASSERT(!prog && !func_decl);
}
OS::Core::GCFunctionValue * OS::Core::newFunctionValue(StackFunction * stack_func, Program * prog, FunctionDecl * func_decl, Value env)
{
GCFunctionValue * func_value = new (allocator->malloc(sizeof(GCFunctionValue) OS_DBG_FILEPOS)) GCFunctionValue();
func_value->type = OS_VALUE_TYPE_FUNCTION;
func_value->prototype = prototypes[PROTOTYPE_FUNCTION];
func_value->prog = prog->retain();
func_value->func_decl = func_decl;
func_value->env = env; // global_vars;
func_value->upvalues = stack_func ? stack_func->locals->retain() : NULL;
func_value->name = NULL;
registerValue(func_value);
// pushValue(func_value);
return func_value;
}
void OS::Core::clearFunctionValue(GCFunctionValue * func_value)
{
OS_ASSERT(func_value->prog && func_value->func_decl); // && func_data->env); // && func_data->self
// value could be already destroyed by gc or will be destroyed soon
// releaseValue(func_data->env);
func_value->env = (GCValue*)NULL;
// releaseValue(func_data->self);
// func_data->self = NULL;
OS_ASSERT(func_value->func_decl);
if(func_value->upvalues){
releaseUpvalues(func_value->upvalues);
func_value->upvalues = NULL;
}
func_value->name = NULL;
func_value->func_decl = NULL;
func_value->prog->release();
func_value->prog = NULL;
// func_value->~GCFunctionValue();
// free(func_value);
}
// =====================================================================
/*
OS::Core::Upvalues::Upvalues()
{
ref_count = 1;
gc_time = -1;
locals = NULL;
is_stack_locals = false;
num_locals = 0;
num_params = 0;
num_extra_params = 0;
arguments = NULL;
rest_arguments = NULL;
num_parents = 0;
}
OS::Core::Upvalues::~Upvalues()
{
OS_ASSERT(!ref_count);
OS_ASSERT(!locals && !arguments && !rest_arguments);
}
*/
OS::Core::Upvalues ** OS::Core::Upvalues::getParents()
{
return (Upvalues**)(this + 1);
}
OS::Core::Upvalues * OS::Core::Upvalues::getParent(int i)
{
OS_ASSERT(i >= 0 && i < num_parents);
return ((Upvalues**)(this+1))[i];
}
OS::Core::Upvalues * OS::Core::Upvalues::retain()
{
ref_count++;
return this;
}
// =====================================================================
#if 0
OS::Core::StackFunction::StackFunction()
{
/*
func = NULL;
self = NULL;
locals = NULL;
caller_stack_pos = 0;
locals_stack_pos = 0;
opcode_stack_pos = 0;
bottom_stack_pos = 0;
need_ret_values = 0;
opcodes_offs = 0;
*/
}
OS::Core::StackFunction::~StackFunction()
{
OS_ASSERT(!func && !self && !locals);
}
#endif
// =====================================================================
OS::Core::Value::Value()
{
v.value = NULL;
type = OS_VALUE_TYPE_NULL;
}
OS::Core::Value::Value(bool val)
{
v.boolean = val;
type = OS_VALUE_TYPE_BOOL;
}
OS::Core::Value::Value(OS_INT32 val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
}
OS::Core::Value::Value(OS_INT64 val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
}
OS::Core::Value::Value(float val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
}
OS::Core::Value::Value(double val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
}
OS::Core::Value::Value(int val, const WeakRef&)
{
v.value_id = val;
type = OS_VALUE_TYPE_WEAKREF;
}
OS::Core::Value::Value(GCValue * val)
{
if(val){
v.value = val;
type = val->type;
}else{
v.value = NULL;
type = OS_VALUE_TYPE_NULL;
}
}
OS::Core::Value& OS::Core::Value::operator=(GCValue * val)
{
if(val){
v.value = val;
type = val->type;
}else{
v.value = NULL;
type = OS_VALUE_TYPE_NULL;
}
return *this;
}
OS::Core::Value& OS::Core::Value::operator=(bool val)
{
v.boolean = val;
type = OS_VALUE_TYPE_BOOL;
return *this;
}
OS::Core::Value& OS::Core::Value::operator=(OS_INT32 val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
return *this;
}
OS::Core::Value& OS::Core::Value::operator=(OS_INT64 val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
return *this;
}
OS::Core::Value& OS::Core::Value::operator=(float val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
return *this;
}
OS::Core::Value& OS::Core::Value::operator=(double val)
{
v.number = (OS_NUMBER)val;
type = OS_VALUE_TYPE_NUMBER;
return *this;
}
void OS::Core::Value::clear()
{
v.value = NULL;
type = OS_VALUE_TYPE_NULL;
}
OS::Core::GCValue * OS::Core::Value::getGCValue() const
{
switch(type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
OS_ASSERT(v.value);
return v.value;
}
return NULL;
}
bool OS::Core::Value::isNull() const
{
return type == OS_VALUE_TYPE_NULL;
}
bool OS::Core::Value::isFunction() const
{
switch(type){
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return true;
}
return false;
}
bool OS::Core::Value::isUserdata() const
{
switch(type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return true;
}
return false;
}
// =====================================================================
OS::Core::ValueRetained::ValueRetained(): super()
{
}
OS::Core::ValueRetained::ValueRetained(bool val): super(val)
{
// retain();
}
OS::Core::ValueRetained::ValueRetained(OS_FLOAT val): super(val)
{
// retain();
}
OS::Core::ValueRetained::ValueRetained(int val): super(val)
{
// retain();
}
OS::Core::ValueRetained::ValueRetained(int val, const WeakRef& wr): super(val, wr)
{
// retain();
}
OS::Core::ValueRetained::ValueRetained(GCValue * val): super(val)
{
// retain();
if(val){
val->external_ref_count++;
}
}
OS::Core::ValueRetained::ValueRetained(Value b): super(b)
{
retain();
}
OS::Core::ValueRetained::~ValueRetained()
{
release();
}
OS::Core::ValueRetained& OS::Core::ValueRetained::operator=(Value b)
{
release();
super::operator=(b);
retain();
return *this;
}
void OS::Core::ValueRetained::clear()
{
release();
super::clear();
}
void OS::Core::ValueRetained::retain()
{
switch(type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
OS_ASSERT(v.value);
v.value->external_ref_count++;
break;
}
}
void OS::Core::ValueRetained::release()
{
switch(type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
OS_ASSERT(v.value && v.value->external_ref_count > 0);
v.value->external_ref_count--;
if(v.value->gc_color == GC_WHITE){
v.value->gc_color = GC_BLACK;
}
break;
}
}
// =====================================================================
OS::Core::GCValue::GCValue()
{
value_id = 0;
external_ref_count = 0;
hash_next = NULL;
prototype = NULL;
table = NULL;
gc_grey_next = NULL;
#ifdef OS_DEBUG
gc_time = -1;
#endif
gc_color = GC_WHITE;
type = OS_VALUE_TYPE_NULL;
is_object_instance = false;
}
OS::Core::GCValue::~GCValue()
{
value_id = 0;
OS_ASSERT(type == OS_VALUE_TYPE_UNKNOWN);
OS_ASSERT(!table);
OS_ASSERT(!hash_next);
OS_ASSERT(!prototype);
OS_ASSERT(gc_color != GC_GREY);
}
// =====================================================================
OS::Core::GCStringValue::GCStringValue(int p_data_size)
{
data_size = p_data_size;
}
OS::Core::GCStringValue * OS::Core::GCStringValue::alloc(OS * allocator, const void * buf, int data_size OS_DBG_FILEPOS_DECL)
{
OS_ASSERT(data_size >= 0);
int alloc_size = data_size + sizeof(GCStringValue) + sizeof(wchar_t) + sizeof(wchar_t)/2;
GCStringValue * string = new (allocator->malloc(alloc_size OS_DBG_FILEPOS_PARAM)) GCStringValue(data_size);
string->type = OS_VALUE_TYPE_STRING;
string->prototype = allocator->core->prototypes[PROTOTYPE_STRING];
OS_BYTE * data_buf = string->toBytes();
OS_MEMCPY(data_buf, buf, data_size);
OS_MEMSET(data_buf + data_size, 0, sizeof(wchar_t) + sizeof(wchar_t)/2);
string->calcHash();
allocator->core->registerValue(string);
#ifdef OS_DEBUG
string->str = string->toChar();
#endif
return string;
}
OS::Core::GCStringValue * OS::Core::GCStringValue::alloc(OS * allocator, const void * buf1, int len1, const void * buf2, int len2 OS_DBG_FILEPOS_DECL)
{
OS_ASSERT(len1 >= 0 && len2 >= 0);
int alloc_size = len1 + len2 + sizeof(GCStringValue) + sizeof(wchar_t) + sizeof(wchar_t)/2;
GCStringValue * string = new (allocator->malloc(alloc_size OS_DBG_FILEPOS_PARAM)) GCStringValue(len1 + len2);
string->type = OS_VALUE_TYPE_STRING;
string->prototype = allocator->core->prototypes[PROTOTYPE_STRING];
OS_BYTE * data_buf = string->toBytes();
OS_MEMCPY(data_buf, buf1, len1); data_buf += len1;
OS_MEMCPY(data_buf, buf2, len2); data_buf += len2;
OS_MEMSET(data_buf, 0, sizeof(wchar_t) + sizeof(wchar_t)/2);
string->calcHash();
allocator->core->registerValue(string);
#ifdef OS_DEBUG
string->str = string->toChar();
#endif
return string;
}
/*
OS::Core::GCStringValue * OS::Core::GCStringValue::alloc(OS * allocator, const void * buf1, int len1,
const void * buf2, int len2, const void * buf3, int len3)
{
OS_ASSERT(len1 >= 0 && len2 >= 0 && len3 >= 0);
int alloc_size = len1 + len2 + len3 + sizeof(GCStringValue) + sizeof(wchar_t) + sizeof(wchar_t)/2;
GCStringValue * string = new (allocator->malloc(alloc_size)) GCStringValue(len1 + len2 + len3);
string->type = OS_VALUE_TYPE_STRING;
string->prototype = allocator->core->prototypes[PROTOTYPE_STRING];
OS_BYTE * data_buf = string->toBytes();
OS_MEMCPY(data_buf, buf1, len1); data_buf += len1;
OS_MEMCPY(data_buf, buf2, len2); data_buf += len2;
OS_MEMCPY(data_buf, buf3, len3); data_buf += len3;
OS_MEMSET(data_buf, 0, sizeof(wchar_t) + sizeof(wchar_t)/2);
string->calcHash();
allocator->core->registerValue(string);
return string;
}
*/
OS::Core::GCStringValue * OS::Core::GCStringValue::alloc(OS * allocator, GCStringValue * a, GCStringValue * b OS_DBG_FILEPOS_DECL)
{
return alloc(allocator, a->toMemory(), a->data_size, b->toMemory(), b->data_size OS_DBG_FILEPOS_PARAM);
}
/*
OS::Core::GCStringValue * OS::Core::GCStringValue::alloc(OS * allocator, GCStringValue * a, GCStringValue * b, GCStringValue * c)
{
return alloc(allocator, a->toMemory(), a->data_size, b->toMemory(), b->data_size, c->toMemory(), c->data_size);
}
*/
bool OS::Core::GCStringValue::isNumber(OS_NUMBER* p_val) const
{
const OS_CHAR * str = toChar();
const OS_CHAR * end = str + getLen();
OS_FLOAT val;
if(Utils::parseFloat(str, val) && (str == end || (*str==OS_TEXT('f') && str+1 == end))){
if(p_val) *p_val = (OS_NUMBER)val;
return true;
}
if(p_val) *p_val = 0;
return false;
}
int OS::Core::GCStringValue::cmp(GCStringValue * string) const
{
if(this == string){
return 0;
}
return Utils::cmp(toBytes(), data_size, string->toBytes(), string->data_size);
}
int OS::Core::GCStringValue::cmp(const OS_CHAR * str) const
{
return cmp(str, OS_STRLEN(str));
}
int OS::Core::GCStringValue::cmp(const OS_CHAR * str, int len) const
{
return Utils::cmp(toBytes(), data_size, str, len * sizeof(OS_CHAR));
}
void OS::Core::GCStringValue::calcHash()
{
hash = Utils::keyToHash(toBytes(), data_size);
}
// =====================================================================
bool OS::Core::valueToBool(const Value& val)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
return false;
case OS_VALUE_TYPE_BOOL:
return val.v.boolean ? true : false;
case OS_VALUE_TYPE_NUMBER:
// return val->value.number && !OS_ISNAN(val->value.number);
return !OS_ISNAN((OS_FLOAT)val.v.number);
// case OS_VALUE_TYPE_STRING:
// return val->value.string_data->data_size > 0;
// case OS_VALUE_TYPE_OBJECT:
// case OS_VALUE_TYPE_ARRAY:
// return val->table ? val->table->count : 0;
}
return true;
}
OS_INT OS::Core::valueToInt(const Value& val, bool valueof_enabled)
{
return (OS_INT)valueToNumber(val, valueof_enabled);
}
OS_INT OS::Core::Compiler::Expression::toInt()
{
return (OS_INT)toNumber();
}
OS_NUMBER OS::Core::Compiler::Expression::toNumber()
{
switch(type){
case EXP_TYPE_CONST_NULL:
return 0;
case EXP_TYPE_CONST_STRING:
return token->str.toNumber();
case EXP_TYPE_CONST_NUMBER:
return (OS_NUMBER)token->getFloat();
case EXP_TYPE_CONST_TRUE:
return 1;
case EXP_TYPE_CONST_FALSE:
return 0;
}
OS_ASSERT(false);
return 0;
}
OS_NUMBER OS::Core::valueToNumber(const Value& val, bool valueof_enabled)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
return 0; // nan_float;
case OS_VALUE_TYPE_BOOL:
return (OS_NUMBER)val.v.boolean;
case OS_VALUE_TYPE_NUMBER:
return val.v.number;
case OS_VALUE_TYPE_STRING:
{
OS_NUMBER fval;
if(val.v.string->isNumber(&fval)){
return fval;
}
return 0; // nan_float;
}
}
if(valueof_enabled){
pushValueOf(val);
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {this};
return valueToNumber(stack_values.lastElement(), false);
}
return 0;
}
bool OS::Core::isValueNumber(Value val, OS_NUMBER * out)
{
switch(val.type){
/*
case OS_VALUE_TYPE_NULL:
if(out){
*out = 0;
}
return true;
*/
case OS_VALUE_TYPE_BOOL:
if(out){
*out = (OS_NUMBER)val.v.boolean;
}
return true;
case OS_VALUE_TYPE_NUMBER:
if(out){
*out = (OS_NUMBER)val.v.number;
}
return true;
case OS_VALUE_TYPE_STRING:
return val.v.string->isNumber(out);
}
if(out){
*out = 0;
}
return false;
}
OS::Core::String OS::Core::Compiler::Expression::toString()
{
switch(type){
case EXP_TYPE_CONST_NULL:
// return String(getAllocator());
return String(getAllocator(), OS_TEXT("null"));
case EXP_TYPE_CONST_STRING:
return token->str;
case EXP_TYPE_CONST_NUMBER:
// OS_ASSERT(token->str.toFloat() == token->getFloat());
// return token->str;
return String(getAllocator(), token->getFloat());
case EXP_TYPE_CONST_TRUE:
// return String(getAllocator(), OS_TEXT("1"));
return String(getAllocator(), OS_TEXT("true"));
case EXP_TYPE_CONST_FALSE:
// return String(getAllocator());
return String(getAllocator(), OS_TEXT("false"));
}
OS_ASSERT(false);
return String(getAllocator());
}
OS::Core::String OS::Core::valueToString(Value val, bool valueof_enabled)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
// return String(allocator);
return strings->syntax_null;
case OS_VALUE_TYPE_BOOL:
// return val->value.boolean ? String(allocator, OS_TEXT("1")) : String(allocator);
return val.v.boolean ? strings->syntax_true : strings->syntax_false;
case OS_VALUE_TYPE_NUMBER:
return String(allocator, val.v.number, OS_AUTO_PRECISION);
case OS_VALUE_TYPE_STRING:
return String(val.v.string);
}
if(valueof_enabled){
/*
Value * func = getPropertyValue(val, PropertyIndex(strings->__tostring, PropertyIndex::KeepStringIndex()), prototype_enabled);
if(func){
pushValue(func);
pushValue(val);
call(0, 1);
OS_ASSERT(stack_values.count > 0);
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {this};
return valueToString(stack_values.lastElement(), false);
}
*/
pushValueOf(val);
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {this};
return valueToString(stack_values.lastElement(), false);
}
return String(allocator);
}
bool OS::Core::isValueString(Value val, String * out)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
if(out){
// *out = String(allocator);
*out = strings->syntax_null;
}
return false;
case OS_VALUE_TYPE_BOOL:
if(out){
// *out = String(allocator, val->value.boolean ? OS_TEXT("1") : OS_TEXT(""));
*out = val.v.boolean ? strings->syntax_true : strings->syntax_false;
}
return true;
case OS_VALUE_TYPE_NUMBER:
if(out){
*out = String(allocator, val.v.number, OS_AUTO_PRECISION);
}
return true;
case OS_VALUE_TYPE_STRING:
if(out){
*out = String(val.v.string);
}
return true;
// case OS_VALUE_TYPE_OBJECT:
// case OS_VALUE_TYPE_ARRAY:
// return String(this, (OS_INT)(val->table ? val->table->count : 0));
}
if(out){
*out = String(allocator);
}
return false;
}
bool OS::Core::isValueString(Value val, OS::String * out)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
if(out){
// *out = String(allocator);
*out = strings->syntax_null;
}
return false;
case OS_VALUE_TYPE_BOOL:
if(out){
// *out = String(allocator, val->value.boolean ? OS_TEXT("1") : OS_TEXT(""));
*out = val.v.boolean ? strings->syntax_true : strings->syntax_false;
}
return true;
case OS_VALUE_TYPE_NUMBER:
if(out){
*out = String(allocator, val.v.number, OS_AUTO_PRECISION);
}
return true;
case OS_VALUE_TYPE_STRING:
if(out){
*out = String(val.v.string);
}
return true;
// case OS_VALUE_TYPE_OBJECT:
// case OS_VALUE_TYPE_ARRAY:
// return String(this, (OS_INT)(val->table ? val->table->count : 0));
}
if(out){
*out = String(allocator);
}
return false;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::StringRefs::StringRefs()
{
head_mask = 0;
heads = NULL;
count = 0;
}
OS::Core::StringRefs::~StringRefs()
{
OS_ASSERT(count == 0);
OS_ASSERT(!heads);
}
void OS::Core::registerStringRef(StringRef * str_ref)
{
if((string_refs.count>>HASH_GROW_SHIFT) >= string_refs.head_mask){
int new_size = string_refs.heads ? (string_refs.head_mask+1) * 2 : 32;
int alloc_size = sizeof(StringRef*) * new_size;
StringRef ** new_heads = (StringRef**)malloc(alloc_size OS_DBG_FILEPOS);
OS_ASSERT(new_heads);
OS_MEMSET(new_heads, 0, alloc_size);
StringRef ** old_heads = string_refs.heads;
int old_mask = string_refs.head_mask;
string_refs.heads = new_heads;
string_refs.head_mask = new_size-1;
if(old_heads){
for(int i = 0; i <= old_mask; i++){
for(StringRef * str_ref = old_heads[i], * next; str_ref; str_ref = next){
next = str_ref->hash_next;
int slot = str_ref->string_hash & string_refs.head_mask;
str_ref->hash_next = string_refs.heads[slot];
string_refs.heads[slot] = str_ref;
}
}
free(old_heads);
}
}
int slot = str_ref->string_hash & string_refs.head_mask;
str_ref->hash_next = string_refs.heads[slot];
string_refs.heads[slot] = str_ref;
string_refs.count++;
}
void OS::Core::unregisterStringRef(StringRef * str_ref)
{
int slot = str_ref->string_hash & string_refs.head_mask;
StringRef * cur = string_refs.heads[slot], * prev = NULL;
for(; cur; prev = cur, cur = cur->hash_next){
if(cur == str_ref){
if(prev){
prev->hash_next = cur->hash_next;
}else{
string_refs.heads[slot] = cur->hash_next;
}
OS_ASSERT(string_refs.count > 0);
string_refs.count--;
cur->hash_next = NULL;
return;
}
}
OS_ASSERT(false);
}
void OS::Core::deleteStringRefs()
{
if(!string_refs.heads){
return;
}
for(int i = 0; i <= string_refs.head_mask; i++){
while(string_refs.heads[i]){
StringRef * cur = string_refs.heads[i];
string_refs.heads[i] = cur->hash_next;
free(cur);
}
}
free(string_refs.heads);
string_refs.heads = NULL;
string_refs.head_mask = 0;
string_refs.count = 0;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::UserptrRefs::UserptrRefs()
{
head_mask = 0;
heads = NULL;
count = 0;
}
OS::Core::UserptrRefs::~UserptrRefs()
{
OS_ASSERT(count == 0);
OS_ASSERT(!heads);
}
void OS::Core::registerUserptrRef(UserptrRef * user_pointer_ref)
{
if((userptr_refs.count>>HASH_GROW_SHIFT) >= userptr_refs.head_mask){
int new_size = userptr_refs.heads ? (userptr_refs.head_mask+1) * 2 : 32;
int alloc_size = sizeof(UserptrRef*) * new_size;
UserptrRef ** new_heads = (UserptrRef**)malloc(alloc_size OS_DBG_FILEPOS);
OS_ASSERT(new_heads);
OS_MEMSET(new_heads, 0, alloc_size);
UserptrRef ** old_heads = userptr_refs.heads;
int old_mask = userptr_refs.head_mask;
userptr_refs.heads = new_heads;
userptr_refs.head_mask = new_size-1;
if(old_heads){
for(int i = 0; i <= old_mask; i++){
for(UserptrRef * user_pointer_ref = old_heads[i], * next; user_pointer_ref; user_pointer_ref = next){
next = user_pointer_ref->hash_next;
int slot = user_pointer_ref->userptr_hash & userptr_refs.head_mask;
user_pointer_ref->hash_next = userptr_refs.heads[slot];
userptr_refs.heads[slot] = user_pointer_ref;
}
}
free(old_heads);
}
}
int slot = user_pointer_ref->userptr_hash & userptr_refs.head_mask;
user_pointer_ref->hash_next = userptr_refs.heads[slot];
userptr_refs.heads[slot] = user_pointer_ref;
userptr_refs.count++;
}
void OS::Core::unregisterUserptrRef(UserptrRef * userptr_ref)
{
int slot = userptr_ref->userptr_hash & userptr_refs.head_mask;
UserptrRef * cur = userptr_refs.heads[slot], * prev = NULL;
for(; cur; prev = cur, cur = cur->hash_next){
if(cur == userptr_ref){
if(prev){
prev->hash_next = cur->hash_next;
}else{
userptr_refs.heads[slot] = cur->hash_next;
}
OS_ASSERT(userptr_refs.count > 0);
userptr_refs.count--;
cur->hash_next = NULL;
return;
}
}
OS_ASSERT(false);
}
void OS::Core::unregisterUserptrRef(void * ptr, int value_id)
{
if(userptr_refs.count > 0){
OS_ASSERT(userptr_refs.heads && userptr_refs.head_mask);
int hash = (int)(intptr_t)ptr;
int slot = hash & userptr_refs.head_mask;
UserptrRef * userptr_ref = userptr_refs.heads[slot];
for(UserptrRef * prev = NULL, * next; userptr_ref; userptr_ref = next){
next = userptr_ref->hash_next;
if(userptr_ref->userptr_value_id == value_id){
if(!prev){
userptr_refs.heads[slot] = next;
}else{
prev->hash_next = next;
}
free(userptr_ref);
userptr_refs.count--;
return;
}
}
}
}
void OS::Core::deleteUserptrRefs()
{
if(!userptr_refs.heads){
return;
}
for(int i = 0; i <= userptr_refs.head_mask; i++){
while(userptr_refs.heads[i]){
UserptrRef * cur = userptr_refs.heads[i];
userptr_refs.heads[i] = cur->hash_next;
free(cur);
}
}
free(userptr_refs.heads);
userptr_refs.heads = NULL;
userptr_refs.head_mask = 0;
userptr_refs.count = 0;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::Values::Values()
{
head_mask = 0; // OS_DEF_VALUES_HASH_SIZE-1;
heads = NULL; // new Value*[OS_DEF_VALUES_HASH_SIZE];
// OS_ASSERT(heads);
next_id = 1;
count = 0;
}
OS::Core::Values::~Values()
{
OS_ASSERT(count == 0);
OS_ASSERT(!heads);
// delete [] heads;
}
void OS::Core::registerValue(GCValue * value)
{
value->value_id = values.next_id++;
if((values.count>>HASH_GROW_SHIFT) >= values.head_mask){
int new_size = values.heads ? (values.head_mask+1) * 2 : 32;
int alloc_size = sizeof(GCValue*) * new_size;
GCValue ** new_heads = (GCValue**)malloc(alloc_size OS_DBG_FILEPOS); // new Value*[new_size];
OS_ASSERT(new_heads);
OS_MEMSET(new_heads, 0, alloc_size);
GCValue ** old_heads = values.heads;
int old_mask = values.head_mask;
values.heads = new_heads;
values.head_mask = new_size-1;
if(old_heads){
for(int i = 0; i <= old_mask; i++){
for(GCValue * value = old_heads[i], * next; value; value = next){
gcAddToGreyList(value);
next = value->hash_next;
int slot = value->value_id & values.head_mask;
value->hash_next = values.heads[slot];
values.heads[slot] = value;
}
}
// delete [] old_heads;
free(old_heads);
}
if(gc_values_head_index >= 0){
// restart gc ASAP
gc_values_head_index = -1;
gc_start_next_values = 0;
gc_continuous = false;
gc_step_size_auto_mult *= 4.0f;
}
}
int slot = value->value_id & values.head_mask;
value->hash_next = values.heads[slot];
values.heads[slot] = value;
values.count++;
num_created_values++;
gcAddToGreyList(value);
// value->gc_color = GC_BLACK;
gcStepIfNeeded();
}
OS::Core::GCValue * OS::Core::unregisterValue(int value_id)
{
int slot = value_id & values.head_mask;
GCValue * value = values.heads[slot], * prev = NULL;
for(; value; prev = value, value = value->hash_next){
if(value->value_id == value_id){
if(prev){
prev->hash_next = value->hash_next;
}else{
values.heads[slot] = value->hash_next;
}
OS_ASSERT(values.count > 0);
values.count--;
value->hash_next = NULL;
return value;
}
}
return NULL;
}
void OS::Core::deleteValues(bool del_ref_counted_also)
{
if(values.heads && values.count > 0){
while(true){
for(int i = 0; i <= values.head_mask; i++){
#if 0
for(GCValue * value; value = values.heads[i]; ){
deleteValue(value);
}
#else
for(GCValue * value = values.heads[i], * next; value; value = next){
next = value->hash_next;
if(del_ref_counted_also || !value->external_ref_count){
deleteValue(value);
}
}
#endif
}
if(!values.count || !del_ref_counted_also){
break;
}
}
}
if(values.heads && values.count == 0){
free(values.heads);
values.heads = NULL;
values.head_mask = 0;
values.next_id = 1;
}
}
OS::Core::GCValue * OS::Core::Values::get(int value_id)
{
int slot = value_id & head_mask;
for(GCValue * value = heads[slot]; value; value = value->hash_next){
if(value->value_id == value_id){
return value;
}
}
return NULL;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::Core::Strings::Strings(OS * allocator)
:
__construct(allocator, OS_TEXT("__construct")),
// __destruct(allocator, OS_TEXT("__destruct")),
__object(allocator, OS_TEXT("__object")),
__get(allocator, OS_TEXT("__get")),
__set(allocator, OS_TEXT("__set")),
__getAt(allocator, OS_TEXT("__get@")),
__setAt(allocator, OS_TEXT("__set@")),
__del(allocator, OS_TEXT("__del")),
__delAt(allocator, OS_TEXT("__del@")),
__getempty(allocator, OS_TEXT("__getempty")),
__setempty(allocator, OS_TEXT("__setempty")),
__delempty(allocator, OS_TEXT("__delempty")),
__getdim(allocator, OS_TEXT("__getdim")),
__setdim(allocator, OS_TEXT("__setdim")),
__deldim(allocator, OS_TEXT("__deldim")),
__cmp(allocator, OS_TEXT("__cmp")),
__iter(allocator, OS_TEXT("__iter")),
// __tostring(allocator, OS_TEXT("__tostring")),
__valueof(allocator, OS_TEXT("__valueof")),
/*
__booleanof(allocator, OS_TEXT("__booleanof")),
__numberof(allocator, OS_TEXT("__numberof")),
__stringof(allocator, OS_TEXT("__stringof")),
__arrayof(allocator, OS_TEXT("__arrayof")),
__objectof(allocator, OS_TEXT("__objectof")),
__userdataof(allocator, OS_TEXT("__userdataof")),
__functionof(allocator, OS_TEXT("__functionof")),
*/
__clone(allocator, OS_TEXT("__clone")),
__concat(allocator, OS_TEXT("__concat")),
__bitand(allocator, OS_TEXT("__bitand")),
__bitor(allocator, OS_TEXT("__bitor")),
__bitxor(allocator, OS_TEXT("__bitxor")),
__bitnot(allocator, OS_TEXT("__bitnot")),
__plus(allocator, OS_TEXT("__plus")),
__neg(allocator, OS_TEXT("__neg")),
__len(allocator, OS_TEXT("__len")),
__add(allocator, OS_TEXT("__add")),
__sub(allocator, OS_TEXT("__sub")),
__mul(allocator, OS_TEXT("__mul")),
__div(allocator, OS_TEXT("__div")),
__mod(allocator, OS_TEXT("__mod")),
__lshift(allocator, OS_TEXT("__lshift")),
__rshift(allocator, OS_TEXT("__rshift")),
__pow(allocator, OS_TEXT("__pow")),
typeof_null(allocator, OS_TEXT("null")),
typeof_boolean(allocator, OS_TEXT("boolean")),
typeof_number(allocator, OS_TEXT("number")),
typeof_string(allocator, OS_TEXT("string")),
typeof_object(allocator, OS_TEXT("object")),
typeof_array(allocator, OS_TEXT("array")),
typeof_userdata(allocator, OS_TEXT("userdata")),
typeof_function(allocator, OS_TEXT("function")),
syntax_get(allocator, OS_TEXT("get")),
syntax_set(allocator, OS_TEXT("set")),
syntax_super(allocator, OS_TEXT("super")),
syntax_is(allocator, OS_TEXT("is")),
syntax_isprototypeof(allocator, OS_TEXT("isprototypeof")),
syntax_typeof(allocator, OS_TEXT("typeof")),
syntax_valueof(allocator, OS_TEXT("valueof")),
syntax_booleanof(allocator, OS_TEXT("booleanof")),
syntax_numberof(allocator, OS_TEXT("numberof")),
syntax_stringof(allocator, OS_TEXT("stringof")),
syntax_arrayof(allocator, OS_TEXT("arrayof")),
syntax_objectof(allocator, OS_TEXT("objectof")),
syntax_userdataof(allocator, OS_TEXT("userdataof")),
syntax_functionof(allocator, OS_TEXT("functionof")),
syntax_extends(allocator, OS_TEXT("extends")),
syntax_clone(allocator, OS_TEXT("clone")),
syntax_delete(allocator, OS_TEXT("delete")),
syntax_prototype(allocator, OS_TEXT("prototype")),
syntax_var(allocator, OS_TEXT("var")),
syntax_this(allocator, OS_TEXT("this")),
syntax_arguments(allocator, OS_TEXT("arguments")),
syntax_function(allocator, OS_TEXT("function")),
syntax_null(allocator, OS_TEXT("null")),
syntax_true(allocator, OS_TEXT("true")),
syntax_false(allocator, OS_TEXT("false")),
syntax_return(allocator, OS_TEXT("return")),
syntax_class(allocator, OS_TEXT("class")),
syntax_enum(allocator, OS_TEXT("enum")),
syntax_switch(allocator, OS_TEXT("switch")),
syntax_case(allocator, OS_TEXT("case")),
syntax_default(allocator, OS_TEXT("default")),
syntax_if(allocator, OS_TEXT("if")),
syntax_else(allocator, OS_TEXT("else")),
syntax_elseif(allocator, OS_TEXT("elseif")),
syntax_for(allocator, OS_TEXT("for")),
syntax_in(allocator, OS_TEXT("in")),
syntax_break(allocator, OS_TEXT("break")),
syntax_continue(allocator, OS_TEXT("continue")),
syntax_debugger(allocator, OS_TEXT("debugger")),
syntax_debuglocals(allocator, OS_TEXT("debuglocals")),
#ifdef OS_GLOBAL_VAR_ENABLED
var_globals(allocator, OS_GLOBALS_VAR_NAME),
#endif
var_env(allocator, OS_ENV_VAR_NAME),
__dummy__(0)
{
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::MemoryManager::MemoryManager()
{
ref_count = 1;
}
OS::MemoryManager::~MemoryManager()
{
}
OS::MemoryManager * OS::MemoryManager::retain()
{
ref_count++;
return this;
}
void OS::MemoryManager::release()
{
if(--ref_count <= 0){
OS_ASSERT(ref_count == 0);
delete this;
}
}
bool OS::isFileExist(const OS_CHAR * filename)
{
void * f = openFile(filename, "rb");
if(f){
closeFile(f);
return true;
}
return false;
}
void * OS::openFile(const OS_CHAR * filename, const OS_CHAR * mode)\
{
return fopen(filename, mode);
}
int OS::readFile(void * buf, int size, void * f)
{
if(f){
return fread(buf, size, 1, (FILE*)f);
}
return 0;
}
int OS::writeFile(const void * buf, int size, void * f)
{
if(f){
return fwrite(buf, size, 1, (FILE*)f);
}
return 0;
}
int OS::seekFile(void * f, int offset, int whence)
{
if(f){
fseek((FILE*)f, offset, whence);
return ftell((FILE*)f);
}
return 0;
}
void OS::closeFile(void * f)
{
if(f){
fclose((FILE*)f);
}
}
void OS::printf(const OS_CHAR * format, ...)
{
va_list va;
va_start(va, format);
OS_VPRINTF(format, va);
va_end(va);
}
OS::SmartMemoryManager::SmartMemoryManager()
{
allocated_bytes = 0;
max_allocated_bytes = 0;
cached_bytes = 0;
OS_MEMSET(page_desc, 0, sizeof(page_desc));
num_page_desc = 0;
OS_MEMSET(pages, 0, sizeof(pages));
OS_MEMSET(cached_blocks, 0, sizeof(cached_blocks));
#ifdef OS_DEBUG
dbg_mem_list = NULL;
dbg_std_mem_list = NULL;
dbg_breakpoint_id = -1;
#endif
stat_malloc_count = 0;
stat_free_count = 0;
registerPageDesc(sizeof(Core::GCObjectValue), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::GCStringValue), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::GCUserdataValue), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::GCFunctionValue), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::GCCFunctionValue), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::GCCFunctionValue) + sizeof(Core::Value)*4, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Property), OS_MEMORY_MANAGER_PAGE_BLOCKS);
// registerPageDesc(sizeof(Core::StackFunction), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Upvalues), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Upvalues) + sizeof(void*)*4, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Upvalues) + sizeof(void*)*8, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Table), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::Compiler::EXPRESSION_SIZE), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(sizeof(Core::TokenData), OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(8, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(16, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(32, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(64, OS_MEMORY_MANAGER_PAGE_BLOCKS);
registerPageDesc(128, OS_MEMORY_MANAGER_PAGE_BLOCKS/2);
registerPageDesc(256, OS_MEMORY_MANAGER_PAGE_BLOCKS/4);
sortPageDesc();
}
OS::SmartMemoryManager::~SmartMemoryManager()
{
freeCachedMemory(0);
#ifdef OS_DEBUG
{
for(MemBlock * mem = dbg_mem_list; mem; mem = mem->dbg_mem_next){
OS_PRINTF("[LEAK] %d bytes, id: %d, line %d, %s\n", mem->block_size, mem->dbg_id, mem->dbg_line, mem->dbg_filename);
}
}
{
for(StdMemBlock * mem = dbg_std_mem_list; mem; mem = mem->dbg_mem_next){
OS_ASSERT(mem->block_size & 0x80000000);
OS_PRINTF("[LEAK] %d bytes, id: %d, line %d, %s\n", (mem->block_size & ~0x80000000), mem->dbg_id, mem->dbg_line, mem->dbg_filename);
}
}
#endif
// OS_ASSERT(!allocated_bytes && !cached_bytes);
}
#ifdef OS_DEBUG
static const int MEM_MARK_BEGIN = 0xabcdef98;
static const int MEM_MARK_END = 0x3579faec;
static const int FREE_MARK_BEGIN = 0xdabcef98;
static const int FREE_MARK_END = 0x3faec579;
static const int STD_MEM_MARK_BEGIN = 0xaefbcd98;
static const int STD_MEM_MARK_END = 0x35ae79fc;
#define MEM_MARK_END_SIZE sizeof(int)
#else
#define MEM_MARK_END_SIZE 0
#endif
int OS::SmartMemoryManager::comparePageDesc(const void * pa, const void * pb)
{
PageDesc * a = (PageDesc*)pa;
PageDesc * b = (PageDesc*)pb;
return a->block_size - b->block_size;
}
void OS::SmartMemoryManager::sortPageDesc()
{
::qsort(page_desc, num_page_desc, sizeof(page_desc[0]), comparePageDesc);
}
void OS::SmartMemoryManager::registerPageDesc(int block_size, int num_blocks)
{
if(num_page_desc == MAX_PAGE_TYPE_COUNT){
return;
}
if(block_size > 128){
block_size = (block_size + 31) & ~31;
}else if(block_size > 64){
block_size = (block_size + 15) & ~15;
}else if(block_size > 32){
block_size = (block_size + 7) & ~7;
}else{
block_size = (block_size + 3) & ~3;
}
int i;
for(i = 0; i < num_page_desc; i++){
if(page_desc[i].block_size == block_size){
if(page_desc[i].num_blocks < num_blocks){
page_desc[i].num_blocks = num_blocks;
page_desc[i].allocated_bytes = sizeof(Page) + (sizeof(MemBlock) + block_size + MEM_MARK_END_SIZE) * num_blocks;
}
return;
}
}
page_desc[i].block_size = block_size;
page_desc[i].num_blocks = num_blocks;
page_desc[i].allocated_bytes = sizeof(Page) + (sizeof(MemBlock) + block_size + MEM_MARK_END_SIZE) * num_blocks;
num_page_desc++;
}
void * OS::SmartMemoryManager::allocFromCachedBlock(int i OS_DBG_FILEPOS_DECL)
{
#ifdef OS_DEBUG
if(stat_malloc_count == dbg_breakpoint_id){
DEBUG_BREAK;
}
#endif
stat_malloc_count++;
OS_ASSERT(i >= 0 && i < num_page_desc);
CachedBlock * cached_block = cached_blocks[i];
OS_ASSERT(cached_block);
#ifdef OS_DEBUG
OS_ASSERT(cached_block->mark == FREE_MARK_BEGIN);
OS_ASSERT(*(int*)(((OS_BYTE*)((MemBlock*)cached_block+1)) + page_desc[i].block_size) == FREE_MARK_END);
#endif
cached_blocks[i] = cached_block->next;
Page * page = cached_block->page;
OS_ASSERT(page->num_cached_blocks > 0);
page->num_cached_blocks--;
MemBlock * mem_block = (MemBlock*)cached_block;
mem_block->page = page;
mem_block->block_size = page_desc[i].block_size;
#ifdef OS_DEBUG
mem_block->mark = MEM_MARK_BEGIN;
*(int*)(((OS_BYTE*)(mem_block+1)) + mem_block->block_size) = MEM_MARK_END;
mem_block->dbg_filename = dbg_filename;
mem_block->dbg_line = dbg_line;
mem_block->dbg_id = stat_malloc_count-1;
mem_block->dbg_mem_prev = NULL;
mem_block->dbg_mem_next = dbg_mem_list;
if(dbg_mem_list){
dbg_mem_list->dbg_mem_prev = mem_block;
}
dbg_mem_list = mem_block;
#endif
cached_bytes -= mem_block->block_size + sizeof(MemBlock);
void * p = mem_block + 1;
OS_MEMSET(p, 0, mem_block->block_size);
// OS_ASSERT(mem_block->mark == MEM_MARK_BEGIN);
// OS_ASSERT(*(int*)(((OS_BYTE*)(mem_block+1)) + mem_block->block_size) == MEM_MARK_END);
return p;
}
void * OS::SmartMemoryManager::allocFromPageType(int i OS_DBG_FILEPOS_DECL)
{
OS_ASSERT(i >= 0 && i < num_page_desc);
if(cached_blocks[i]){
return allocFromCachedBlock(i OS_DBG_FILEPOS_PARAM);
}
int allocated_bytes = page_desc[i].allocated_bytes;
Page * page = (Page*)stdAlloc(allocated_bytes OS_DBG_FILEPOS);
page->index = i;
page->next_page = pages[i];
pages[i] = page;
page->num_cached_blocks = page_desc[i].num_blocks;
cached_bytes += allocated_bytes;
OS_BYTE * next_page_block = (OS_BYTE*)(page + 1);
for(int j = 0; j < page_desc[i].num_blocks; j++){
CachedBlock * cached_block = (CachedBlock*)next_page_block;
cached_block->page = page;
cached_block->next = cached_blocks[i];
#ifdef OS_DEBUG
cached_block->mark = FREE_MARK_BEGIN;
*(int*)(((OS_BYTE*)((MemBlock*)cached_block+1)) + page_desc[page->index].block_size) = FREE_MARK_END;
OS_MEMSET(cached_block+1, 0xde, page_desc[i].block_size + (sizeof(MemBlock) - sizeof(CachedBlock)));
#endif
cached_blocks[i] = cached_block;
next_page_block += sizeof(MemBlock) + page_desc[i].block_size + MEM_MARK_END_SIZE;
}
return allocFromCachedBlock(i OS_DBG_FILEPOS_PARAM);
}
void OS::SmartMemoryManager::freeMemBlock(MemBlock * mem_block)
{
stat_free_count++;
#ifdef OS_DEBUG
OS_ASSERT(mem_block->mark == MEM_MARK_BEGIN);
OS_ASSERT(*(int*)(((OS_BYTE*)(mem_block+1)) + mem_block->block_size) == MEM_MARK_END);
if(mem_block->dbg_id == dbg_breakpoint_id){
DEBUG_BREAK;
}
if(mem_block == dbg_mem_list){
OS_ASSERT(!mem_block->dbg_mem_prev);
dbg_mem_list = mem_block->dbg_mem_next;
}else{ // if(mem_block->dbg_mem_prev){
OS_ASSERT(mem_block->dbg_mem_prev);
mem_block->dbg_mem_prev->dbg_mem_next = mem_block->dbg_mem_next;
}
if(mem_block->dbg_mem_next){
mem_block->dbg_mem_next->dbg_mem_prev = mem_block->dbg_mem_prev;
}
#endif
Page * page = mem_block->page;
int size = mem_block->block_size;
cached_bytes += size + sizeof(MemBlock);
CachedBlock * cached_block = (CachedBlock*)mem_block;
cached_block->page = page;
cached_block->next = cached_blocks[page->index];
#ifdef OS_DEBUG
cached_block->mark = FREE_MARK_BEGIN;
*(int*)(((OS_BYTE*)((MemBlock*)cached_block+1)) + page_desc[page->index].block_size) = FREE_MARK_END;
OS_MEMSET(cached_block+1, 0xde, size + (sizeof(MemBlock) - sizeof(CachedBlock)));
#endif
cached_blocks[page->index] = cached_block;
page->num_cached_blocks++;
}
void OS::SmartMemoryManager::freeCachedMemory(int new_cached_bytes)
{
if(cached_bytes > new_cached_bytes){
for(int i = num_page_desc-1; i >= 0; i--){
bool found_free_page = false;
int num_blocks = page_desc[i].num_blocks;
CachedBlock * prev_cached_block = NULL, * next_cached_block = NULL;
for(CachedBlock * cached_block = cached_blocks[i]; cached_block; cached_block = next_cached_block){
OS_ASSERT(cached_block->page->index == i);
next_cached_block = cached_block->next;
if(cached_block->page->num_cached_blocks == num_blocks){
found_free_page = true;
if(!prev_cached_block){
cached_blocks[i] = next_cached_block;
}else{
prev_cached_block->next = next_cached_block;
}
// keep prev_cached_block
continue;
}
prev_cached_block = cached_block;
}
if(found_free_page){
Page * prev = NULL, * next;
for(Page * page = pages[i]; page; page = next){
next = page->next_page;
if(page->num_cached_blocks == num_blocks){
if(!prev){
pages[i] = page->next_page;
}else{
prev->next_page = page->next_page;
}
cached_bytes -= page_desc[i].allocated_bytes;
stdFree(page);
// stat_free_count++;
}else{
prev = page;
}
}
if(cached_bytes <= new_cached_bytes){
break;
}
}
}
}
}
void * OS::SmartMemoryManager::stdAlloc(int size OS_DBG_FILEPOS_DECL)
{
#ifdef OS_DEBUG
if(stat_malloc_count == dbg_breakpoint_id){
DEBUG_BREAK;
}
#endif
stat_malloc_count++;
size = (size + 7) & ~7;
StdMemBlock * mem_block = (StdMemBlock*)::malloc(size + sizeof(StdMemBlock) + MEM_MARK_END_SIZE);
if(!mem_block && cached_bytes > 0){
freeCachedMemory(0);
mem_block = (StdMemBlock*)::malloc(size + sizeof(StdMemBlock) + MEM_MARK_END_SIZE);
if(!mem_block){
return NULL;
}
}
#ifdef OS_DEBUG
mem_block->mark = STD_MEM_MARK_BEGIN;
*(int*)(((OS_BYTE*)(mem_block+1)) + size) = STD_MEM_MARK_END;
mem_block->dbg_filename = dbg_filename;
mem_block->dbg_line = dbg_line;
mem_block->dbg_id = stat_malloc_count-1;
mem_block->dbg_mem_prev = NULL;
mem_block->dbg_mem_next = dbg_std_mem_list;
if(dbg_std_mem_list){
dbg_std_mem_list->dbg_mem_prev = mem_block;
}
dbg_std_mem_list = mem_block;
#endif
mem_block->block_size = size | 0x80000000;
allocated_bytes += size + sizeof(StdMemBlock) + MEM_MARK_END_SIZE;
if(max_allocated_bytes < allocated_bytes){
max_allocated_bytes = allocated_bytes;
}
OS_MEMSET(mem_block+1, 0, size);
return mem_block+1;
}
void OS::SmartMemoryManager::stdFree(void * ptr)
{
stat_free_count++;
StdMemBlock * mem_block = (StdMemBlock*)ptr - 1;
OS_ASSERT(mem_block->block_size & 0x80000000);
#ifdef OS_DEBUG
OS_ASSERT(mem_block->mark == STD_MEM_MARK_BEGIN);
OS_ASSERT(*(int*)(((OS_BYTE*)(mem_block+1)) + (mem_block->block_size & ~0x80000000)) == STD_MEM_MARK_END);
if(mem_block->dbg_id == dbg_breakpoint_id){
DEBUG_BREAK;
}
if(mem_block == dbg_std_mem_list){
OS_ASSERT(!mem_block->dbg_mem_prev);
dbg_std_mem_list = mem_block->dbg_mem_next;
}else{ // if(mem_block->dbg_mem_prev){
OS_ASSERT(mem_block->dbg_mem_prev);
mem_block->dbg_mem_prev->dbg_mem_next = mem_block->dbg_mem_next;
}
if(mem_block->dbg_mem_next){
mem_block->dbg_mem_next->dbg_mem_prev = mem_block->dbg_mem_prev;
}
#endif
int size = mem_block->block_size & ~0x80000000;
allocated_bytes -= size + sizeof(StdMemBlock) + MEM_MARK_END_SIZE;
#ifdef OS_DEBUG
OS_MEMSET(ptr, 0xde, size);
#endif
::free(mem_block);
}
void * OS::SmartMemoryManager::malloc(int size OS_DBG_FILEPOS_DECL)
{
if(size <= 0){
return NULL;
}
// stat_malloc_count++;
#if 0
int start = 0, end = num_page_desc-1;
if(size <= page_desc[end].block_size){
for(;;){
if(start >= end){
int block_size = page_desc[start].block_size;
if(size > block_size){
start++;
}
return allocFromPageType(start);
}
int mid = (start + end) / 2;
int block_size = page_desc[mid].block_size;
if(size == block_size){
return allocFromPageType(mid);
}
if(size < block_size){
end = mid - 1;
continue;
}
start = mid + 1;
}
}
#else
if(size <= page_desc[num_page_desc-1].block_size){
for(int i = 0; i < num_page_desc; i++){
if(size <= page_desc[i].block_size){
return allocFromPageType(i OS_DBG_FILEPOS_PARAM);
}
}
}
#endif
return stdAlloc(size OS_DBG_FILEPOS_PARAM);
}
void OS::SmartMemoryManager::free(void * ptr)
{
if(!ptr){
return;
}
// stat_free_count++;
#ifdef OS_DEBUG
int * p = (int*)ptr - 2;
#else
int * p = (int*)ptr - 1;
#endif
int size = p[0];
if(size & 0x80000000){
stdFree(ptr); // p, size & ~0x80000000);
return;
}
MemBlock * mem_block = (MemBlock*)ptr - 1;
OS_ASSERT(mem_block->block_size == size);
freeMemBlock(mem_block);
if(!(stat_free_count % 1024) && cached_bytes > allocated_bytes / 2){
freeCachedMemory(cached_bytes / 2);
}
}
void OS::SmartMemoryManager::setBreakpointId(int id)
{
#ifdef OS_DEBUG
dbg_breakpoint_id = id;
#endif
}
int OS::SmartMemoryManager::getAllocatedBytes()
{
return allocated_bytes;
}
int OS::SmartMemoryManager::getMaxAllocatedBytes()
{
return max_allocated_bytes;
}
int OS::SmartMemoryManager::getCachedBytes()
{
return cached_bytes;
}
// =====================================================================
// =====================================================================
// =====================================================================
OS::OS()
{
ref_count = 1;
memory_manager = NULL;
core = NULL;
#ifdef OS_DEBUG
int mark = 0;
native_stack_start_mark = (int)&mark;
native_stack_max_usage = 0;
#endif
}
OS::~OS()
{
OS_ASSERT(ref_count == 0);
OS_ASSERT(!core && !memory_manager);
// deleteObj(core);
// delete memory_manager;
// memory_manager->release();
}
#ifdef OS_DEBUG
void OS::checkNativeStackUsage(const OS_CHAR * func_name)
{
int mark = 0;
int cur_native_stack_usage = (int)&mark - native_stack_start_mark;
if(cur_native_stack_usage < 0){
// native_stack_start_mark += cur_native_stack_usage;
cur_native_stack_usage = -cur_native_stack_usage;
}
if(native_stack_max_usage < cur_native_stack_usage){
if(cur_native_stack_usage > 1024*10 && cur_native_stack_usage > native_stack_max_usage * 5 / 4){
printf(OS_TEXT("native stack usage: %.1f Kb (%s)\n"), (float)cur_native_stack_usage/1024.0f, func_name);
}
native_stack_max_usage = cur_native_stack_usage;
}
}
#endif
void * OS::malloc(int size OS_DBG_FILEPOS_DECL)
{
return memory_manager->malloc(size OS_DBG_FILEPOS_PARAM);
}
void OS::free(void * p)
{
memory_manager->free(p);
}
void * OS::Core::malloc(int size OS_DBG_FILEPOS_DECL)
{
return allocator->malloc(size OS_DBG_FILEPOS_PARAM);
}
void OS::Core::free(void * p)
{
allocator->free(p);
}
int OS::getAllocatedBytes()
{
return memory_manager->getAllocatedBytes();
}
int OS::getMaxAllocatedBytes()
{
return memory_manager->getMaxAllocatedBytes();
}
int OS::getCachedBytes()
{
return memory_manager->getCachedBytes();
}
void OS::setMemBreakpointId(int id)
{
memory_manager->setBreakpointId(id);
}
bool OS::isTerminated()
{
return core->terminated;
}
int OS::getTerminatedCode()
{
return core->terminated_code;
}
void OS::setTerminated(bool terminated, int code)
{
core->terminated = terminated;
core->terminated_code = code;
}
void OS::resetTerminated()
{
core->terminated = false;
core->terminated_code = 0;
}
OS::Core::Core(OS * p_allocator)
{
allocator = p_allocator;
strings = NULL;
OS_MEMSET(prototypes, 0, sizeof(prototypes));
// string_values_table = NULL;
check_recursion = NULL;
// global_vars = NULL;
// user_pool = NULL;
num_created_values = 0;
num_destroyed_values = 0;
stack_func = NULL;
stack_func_locals = NULL;
num_stack_func_locals = 0;
stack_func_env_index = 0;
stack_func_prog_numbers = NULL;
stack_func_prog_strings = NULL;
settings.create_compiled_file = true;
settings.create_debug_info = true;
settings.create_debug_opcodes = true;
settings.primary_compiled_file = false;
gcInitGreyList();
OS_MEMSET(rand_state, 0, sizeof(rand_state));
rand_next = NULL;
rand_seed = 0;
rand_left = 0;
terminated = false;
terminated_code = 0;
}
OS::Core::~Core()
{
OS_ASSERT(!strings && global_vars.isNull() && user_pool.isNull() && !check_recursion);
for(int i = 0; i < PROTOTYPE_COUNT; i++){
OS_ASSERT(!prototypes[i]);
}
}
OS * OS::create(MemoryManager * manager)
{
return create(new OS(), manager);
}
/*
OS * OS::create(OS * os, MemoryManager * manager)
{
return os->start(manager);
}
*/
OS * OS::start(MemoryManager * manager)
{
if(init(manager)){
return this;
}
delete this;
return NULL;
}
bool OS::init(MemoryManager * p_manager)
{
memory_manager = p_manager ? p_manager : new SmartMemoryManager();
core = new (malloc(sizeof(Core) OS_DBG_FILEPOS)) Core(this);
if(core->init()){
initPreScript();
initGlobalFunctions();
initObjectClass();
initArrayClass();
initStringClass();
initFunctionClass();
initMathModule();
initGCModule();
initLangTokenizerModule();
initPostScript();
return true;
}
return false;
}
void OS::shutdown()
{
core->shutdown();
core->~Core();
free(core);
core = NULL;
memory_manager->release();
memory_manager = NULL;
}
OS * OS::retain()
{
ref_count++;
return this;
}
void OS::release()
{
if(--ref_count <= 0){
OS_ASSERT(ref_count == 0);
shutdown();
delete this;
}
}
bool OS::Core::init()
{
// string_values_table = newTable(OS_DBG_FILEPOS_START);
int i;
for(i = 0; i < PROTOTYPE_COUNT; i++){
prototypes[i] = newObjectValue(NULL);
prototypes[i]->type = OS_VALUE_TYPE_OBJECT;
prototypes[i]->external_ref_count++;
}
check_recursion = newObjectValue();
global_vars = newObjectValue();
user_pool = newObjectValue();
// error_handlers
prototypes[PROTOTYPE_BOOL]->prototype = prototypes[PROTOTYPE_OBJECT];
prototypes[PROTOTYPE_NUMBER]->prototype = prototypes[PROTOTYPE_OBJECT];
prototypes[PROTOTYPE_STRING]->prototype = prototypes[PROTOTYPE_OBJECT];
prototypes[PROTOTYPE_ARRAY]->prototype = prototypes[PROTOTYPE_OBJECT];
prototypes[PROTOTYPE_FUNCTION]->prototype = prototypes[PROTOTYPE_OBJECT];
prototypes[PROTOTYPE_USERDATA]->prototype = prototypes[PROTOTYPE_OBJECT];
strings = new (malloc(sizeof(Strings) OS_DBG_FILEPOS)) Strings(allocator);
setGlobalValue(OS_TEXT("Object"), Value(prototypes[PROTOTYPE_OBJECT]), false, false);
setGlobalValue(OS_TEXT("Boolean"), Value(prototypes[PROTOTYPE_BOOL]), false, false);
setGlobalValue(OS_TEXT("Number"), Value(prototypes[PROTOTYPE_NUMBER]), false, false);
setGlobalValue(OS_TEXT("String"), Value(prototypes[PROTOTYPE_STRING]), false, false);
setGlobalValue(OS_TEXT("Array"), Value(prototypes[PROTOTYPE_ARRAY]), false, false);
setGlobalValue(OS_TEXT("Function"), Value(prototypes[PROTOTYPE_FUNCTION]), false, false);
setGlobalValue(OS_TEXT("Userdata"), Value(prototypes[PROTOTYPE_USERDATA]), false, false);
/*
SAFE usage of user function arguments
so user can use just os->toNumber(-params+3) and so on
if function call has no enough arguments, for example params == 0
then (-params+3) will be not relative offset but absolute offset 3
lets make top OS_TOP_STACK_NULL_VALUES value as null values
*/
for(i = 0; i < OS_TOP_STACK_NULL_VALUES; i++){
pushValue(Value());
}
return true;
}
int OS::Core::compareGCValues(const void * a, const void * b)
{
GCValue * v1 = *(GCValue**)a;
GCValue * v2 = *(GCValue**)b;
if(v1->external_ref_count != v2->external_ref_count){
return v2->external_ref_count - v1->external_ref_count;
}
return v1->value_id - v2->value_id;
}
void OS::Core::shutdown()
{
int i;
OS_ASSERT(stack_values.count >= OS_TOP_STACK_NULL_VALUES);
for(i = 0; i < OS_TOP_STACK_NULL_VALUES; i++){
OS_ASSERT(stack_values[i].type == OS_VALUE_TYPE_NULL);
}
// stack_values.count = 0;
while(call_stack_funcs.count > 0){
StackFunction * stack_func = &call_stack_funcs[--call_stack_funcs.count];
clearStackFunction(stack_func);
}
allocator->vectorClear(call_stack_funcs);
// vectorClear(cache_values);
// gcFull();
gcResetGreyList();
allocator->deleteObj(strings);
// try to finalize the values accurately
Vector<GCValue*> collectedValues;
allocator->vectorReserveCapacity(collectedValues, values.count OS_DBG_FILEPOS);
for(int i = 0; i <= values.head_mask; i++){
for(GCValue * value = values.heads[i]; value; value = value->hash_next){
allocator->vectorAddItem(collectedValues, value OS_DBG_FILEPOS);
}
}
::qsort(collectedValues.buf, collectedValues.count, sizeof(GCValue*), compareGCValues);
for(i = collectedValues.count-1; i >= 0; i--){
deleteValue(collectedValues[i]);
}
allocator->vectorClear(collectedValues);
deleteValues(true); // just clear values.heads
check_recursion = NULL;
global_vars = (GCValue*)NULL;
user_pool = (GCValue*)NULL;
for(i = 0; i < OS_ERROR_LEVELS; i++){
error_handlers[i] = NULL;
}
for(i = 0; i < PROTOTYPE_COUNT; i++){
prototypes[i] = NULL;
}
deleteStringRefs();
deleteUserptrRefs();
if(stack_values.buf){ // it makes sense because of someone could use stack while the finalizing in process
free(stack_values.buf);
stack_values.buf = NULL;
stack_values.capacity = 0;
stack_values.count = 0;
}
OS_ASSERT(!call_stack_funcs.count);
}
OS::String OS::changeFilenameExt(const String& filename, const String& ext)
{
int len = filename.getLen();
for(int i = len-1; i >= 0; i--){
if(filename[i] == OS_TEXT('.')){
return String(this, filename, i, ext, ext.getLen());
}
if(OS_IS_SLASH(filename[i])){
break;
}
}
return String(this, filename, len, ext, ext.getLen());
}
OS::String OS::changeFilenameExt(const String& filename, const OS_CHAR * ext)
{
int len = filename.getLen();
for(int i = len-1; i >= 0; i--){
if(filename[i] == OS_TEXT('.')){
if(OS_STRCMP(filename.toChar()+i, ext) == 0){
return filename;
}
return String(this, filename, i, ext, OS_STRLEN(ext));
}
if(OS_IS_SLASH(filename[i])){
break;
}
}
return String(this, filename, len, ext, OS_STRLEN(ext));
}
OS::String OS::getFilenameExt(const String& filename)
{
return getFilenameExt(filename, filename.getLen());
}
OS::String OS::getFilenameExt(const OS_CHAR * filename)
{
return getFilenameExt(filename, OS_STRLEN(filename));
}
OS::String OS::getFilenameExt(const OS_CHAR * filename, int len)
{
for(int i = len-1; i >= 0; i--){
if(filename[i] == OS_TEXT('.')){
return String(this, filename+i, len-i);
}
if(OS_IS_SLASH(filename[i])){
break;
}
}
return String(this);
}
OS::String OS::getFilename(const String& filename)
{
return getFilename(filename, filename.getLen());
}
OS::String OS::getFilename(const OS_CHAR * filename)
{
return getFilename(filename, OS_STRLEN(filename));
}
OS::String OS::getFilename(const OS_CHAR * filename, int len)
{
for(int i = len-1; i >= 0; i--){
if(OS_IS_SLASH(filename[i])){
return String(this, filename+i+1, len-i-1);
}
}
return String(this, filename, len);
}
OS::String OS::getFilenamePath(const String& filename)
{
return getFilenamePath(filename, filename.getLen());
}
OS::String OS::getFilenamePath(const OS_CHAR * filename)
{
return getFilenamePath(filename, OS_STRLEN(filename));
}
OS::String OS::getFilenamePath(const OS_CHAR * filename, int len)
{
for(int i = len-1; i >= 0; i--){
if(OS_IS_SLASH(filename[i])){
return String(this, filename, i);
}
}
return String(this);
}
bool OS::isAbsolutePath(const String& p_filename)
{
int len = p_filename.getLen();
const OS_CHAR * filename = p_filename;
if(OS_IS_ALPHA(filename[0])){
for(int i = 1; i < len-2; i++){
if(!OS_IS_ALPHA(filename[i])){
return filename[i] == OS_TEXT(':') && OS_IS_SLASH(filename[i+1]);
}
}
}
return len >= 2 && OS_IS_SLASH(filename[0]) && OS_IS_SLASH(filename[1]);
}
OS::String OS::resolvePath(const String& filename, const String& cur_path)
{
String resolved_path = filename;
if(!isAbsolutePath(filename) && cur_path.getLen()){
if(filename.getLen() < cur_path.getLen() || String(this, filename.toChar(), cur_path.getLen()) != cur_path){
resolved_path = cur_path + OS_PATH_SEPARATOR + filename;
}
}
resolved_path = changeFilenameExt(resolved_path, OS_SOURCECODE_EXT);
if(isFileExist(resolved_path)){
return resolved_path;
}
resolved_path = changeFilenameExt(resolved_path, OS_COMPILED_EXT);
if(isFileExist(resolved_path)){
return resolved_path;
}
core->error(OS_E_WARNING, String::format(this, OS_TEXT("filename %s is not resolved"), filename.toChar()));
return String(this);
}
OS::String OS::getCompiledFilename(const OS::String& resolved_filename)
{
return changeFilenameExt(resolved_filename, OS_COMPILED_EXT);
}
OS::String OS::getDebugInfoFilename(const String& resolved_filename)
{
return changeFilenameExt(resolved_filename, OS_DEBUG_INFO_EXT);
}
OS::String OS::getDebugOpcodesFilename(const String& resolved_filename)
{
if(resolved_filename.getDataSize()){
return changeFilenameExt(resolved_filename, OS_DEBUG_OPCODES_EXT);
}
static int num_evals = 0;
return String(this, Core::String::format(this, OS_TEXT("eval-%d%s"), ++num_evals, OS_DEBUG_OPCODES_EXT));
}
OS::String OS::resolvePath(const String& filename)
{
String cur_path(this);
if(core->call_stack_funcs.count > 0){
for(int i = core->call_stack_funcs.count-1; i >= 0; i--){
Core::StackFunction * stack_func = core->call_stack_funcs.buf + i;
if(stack_func->func->prog->filename.getLen() > 0){
cur_path = getFilenamePath(stack_func->func->prog->filename);
break;
}
}
}
return resolvePath(filename, cur_path);
}
OS::EFileUseType OS::checkFileUsage(const String& sourcecode_filename, const String& compiled_filename)
{
return COMPILE_SOURCECODE_FILE;
}
void OS::Core::errorDivisionByZero()
{
error(OS_E_WARNING, OS_TEXT("division by zero"));
}
void OS::Core::error(int code, const OS_CHAR * message)
{
error(code, String(allocator, message));
}
void OS::Core::error(int code, const String& message)
{
Program * prog = NULL;
Program::DebugInfoItem * debug_info = NULL;
for(int i = call_stack_funcs.count-1; i >= 0 && !debug_info; i--){
Core::StackFunction * stack_func = call_stack_funcs.buf + i;
prog = stack_func->func->prog;
if(prog->filename.getLen() > 0){
int opcode_pos = stack_func->opcodes.getPos() + stack_func->func->func_decl->opcodes_pos;
debug_info = prog->getDebugInfo(opcode_pos);
}
}
int error_level = 0;
for(int i = 0; i < OS_ERROR_LEVELS; i++){
if(code & (1<<i)){
error_level = i;
break;
}
}
if(error_handlers[error_level].isFunction()){
pushValue(error_handlers[error_level]);
pushNull();
pushNumber(code);
pushStringValue(message);
if(debug_info){
pushStringValue(prog->filename);
pushNumber(debug_info->line);
call(4, 0);
}else{
call(2, 0);
}
return;
}
const OS_CHAR * error_type = NULL;
switch(code){
case OS_E_WARNING:
error_type = OS_TEXT("WARNING");
break;
default:
case OS_E_ERROR:
error_type = OS_TEXT("ERROR");
code = OS_E_ERROR;
break;
}
if(debug_info){
allocator->printf("[%s] %s (line: %d, pos: %d, token: %s, filename: %s)\n", error_type, message.toChar(), debug_info->line, debug_info->pos,
debug_info->token.toChar(), prog->filename.toChar());
}else{
allocator->printf("[%s] %s\n", error_type, message.toChar());
}
}
void OS::Core::gcInitGreyList()
{
gc_grey_list_first = NULL;
gc_grey_root_initialized = false;
gc_start_allocated_bytes = 0;
gc_max_allocated_bytes = 0;
gc_keep_heap_count = 0;
gc_continuous_count = 0;
gc_continuous = false;
gc_values_head_index = -1;
gc_time = 0;
gc_in_process = false;
gc_grey_added_count = 0;
// gc_grey_removed_count = 0;
gc_start_values_mult = 1.5f;
gc_step_size_mult = 0.005f;
gc_step_size_auto_mult = 1.0f;
gc_start_next_values = 16;
gc_step_size = 0;
}
void OS::Core::gcResetGreyList()
{
while(gc_grey_list_first){
gcRemoveFromGreyList(gc_grey_list_first);
}
gc_grey_root_initialized = false;
// OS_ASSERT(gc_grey_list.gc_grey_next == (Value*)&gc_grey_list);
// OS_ASSERT(gc_grey_list.gc_grey_prev == (Value*)&gc_grey_list);
}
void OS::Core::gcMarkList(int step_size)
{
if(step_size < 16){
step_size = 16;
}
for(; step_size > 0 && gc_grey_list_first; step_size--){
gcMarkValue(gc_grey_list_first);
}
}
void OS::Core::gcMarkTable(Table * table)
{
Property * prop = table->first, * prop_next;
for(; prop; prop = prop_next){
prop_next = prop->next;
if(prop->index.type == OS_VALUE_TYPE_WEAKREF){
OS_ASSERT(false);
if(!values.get(prop->index.v.value_id)){
PropertyIndex index = *prop;
deleteTableProperty(table, index);
continue;
}
}
if(prop->value.type == OS_VALUE_TYPE_WEAKREF){
if(!values.get(prop->value.v.value_id)){
PropertyIndex index = *prop;
deleteTableProperty(table, index);
continue;
}
}
gcAddToGreyList(prop->index);
gcAddToGreyList(prop->value);
}
}
void OS::Core::gcMarkProgram(Program * prog)
{
/* if(prog->gc_time == gc_time){
return;
}
prog->gc_time = gc_time; */
/* for(int i = 0; i < prog->num_strings; i++){
gcAddToGreyList(prog->const_strings[i]);
} */
}
void OS::Core::gcMarkUpvalues(Upvalues * upvalues)
{
if(upvalues->gc_time == gc_time){
return;
}
upvalues->gc_time = gc_time;
int i;
for(i = 0; i < upvalues->num_locals; i++){
gcAddToGreyList(upvalues->locals[i]);
}
for(i = 0; i < upvalues->num_parents; i++){
gcMarkUpvalues(upvalues->getParent(i));
}
}
void OS::Core::gcMarkStackFunction(StackFunction * stack_func)
{
OS_ASSERT(stack_func->func && stack_func->func->type == OS_VALUE_TYPE_FUNCTION);
gcAddToGreyList(stack_func->func);
gcAddToGreyList(stack_func->self);
if(stack_func->self_for_proto){
gcAddToGreyList(stack_func->self_for_proto);
}
gcMarkUpvalues(stack_func->locals);
if(stack_func->arguments){
gcAddToGreyList(stack_func->arguments);
}
if(stack_func->rest_arguments){
gcAddToGreyList(stack_func->rest_arguments);
}
}
void OS::Core::gcAddToGreyList(Value val)
{
switch(val.type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
gcAddToGreyList(val.v.value);
break;
}
}
void OS::Core::gcAddToGreyList(GCValue * value)
{
if(value->gc_color != GC_WHITE){
return;
}
// OS_ASSERT(!value->gc_grey_next && !value->gc_grey_prev);
OS_ASSERT(!value->gc_grey_next);
// gc_grey_list.insertEnd(value);
value->gc_grey_next = gc_grey_list_first;
gc_grey_list_first = value;
value->gc_color = GC_GREY;
gc_grey_added_count++;
}
void OS::Core::gcRemoveFromGreyList(GCValue * value)
{
// OS_ASSERT(value->gc_grey_next && value->gc_grey_prev);
OS_ASSERT(value->gc_color == GC_GREY);
OS_ASSERT(gc_grey_list_first == value);
// gc_grey_list.remove(value);
gc_grey_list_first = value->gc_grey_next;
value->gc_grey_next = NULL;
value->gc_color = GC_BLACK;
// gc_grey_removed_count++;
}
void OS::Core::gcMarkValue(GCValue * value)
{
gcRemoveFromGreyList(value);
if(value->prototype){
gcAddToGreyList(value->prototype);
}
if(value->table){
gcMarkTable(value->table);
}
switch(value->type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
default:
OS_ASSERT(false);
break;
case OS_VALUE_TYPE_STRING:
OS_ASSERT(dynamic_cast<GCStringValue*>(value));
break;
case OS_VALUE_TYPE_OBJECT:
OS_ASSERT(dynamic_cast<GCObjectValue*>(value));
break;
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<GCArrayValue*>(value));
GCArrayValue * arr = (GCArrayValue*)value;
for(int i = 0; i < arr->values.count; i++){
gcAddToGreyList(arr->values[i]);
}
break;
}
case OS_VALUE_TYPE_FUNCTION:
{
OS_ASSERT(dynamic_cast<GCFunctionValue*>(value));
GCFunctionValue * func_value = (GCFunctionValue*)value;
gcMarkProgram(func_value->prog);
gcAddToGreyList(func_value->env);
if(func_value->upvalues){
gcMarkUpvalues(func_value->upvalues);
}
if(func_value->name){
gcAddToGreyList(func_value->name);
}
break;
}
case OS_VALUE_TYPE_CFUNCTION:
{
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(value));
GCCFunctionValue * func_value = (GCCFunctionValue*)value;
Value * closure_values = (Value*)(func_value + 1);
for(int i = 0; i < func_value->num_closure_values; i++){
gcAddToGreyList(closure_values[i]);
}
if(func_value->name){
gcAddToGreyList(func_value->name);
}
break;
}
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
OS_ASSERT(dynamic_cast<GCUserdataValue*>(value));
break;
}
}
void OS::onEnterGC()
{
}
void OS::onExitGC()
{
}
int OS::Core::gcStep()
{
// return OS_GC_PHASE_MARK;
if(gc_in_process){
return OS_GC_PHASE_MARK;
}
struct GCTouch {
Core * core;
GCTouch(Core * p_core)
{
core = p_core; core->gc_in_process = true;
core->allocator->onEnterGC();
}
~GCTouch()
{
core->gc_in_process = false;
core->allocator->onExitGC();
}
} gc_touch(this);
if(values.count == 0){
gc_values_head_index = -1;
gc_grey_root_initialized = false;
gc_continuous = false;
return OS_GC_PHASE_MARK;
}
int step_size = gc_step_size;
if(gc_values_head_index >= 0){
OS_ASSERT(gc_values_head_index <= values.head_mask);
int i = gc_values_head_index;
step_size += 2; // step_size/16;
for(; i <= values.head_mask && step_size > 0; i++){
for(GCValue * value = values.heads[i], * next; value; value = next, step_size--){
next = value->hash_next;
if(value->gc_color == GC_WHITE && !value->external_ref_count){
OS_ASSERT(!isValueUsed(value));
deleteValue(value);
if(gc_values_head_index < 0){
return OS_GC_PHASE_MARK;
}
}else if(value->gc_color == GC_BLACK){
value->gc_color = GC_WHITE;
}
}
}
if(i <= values.head_mask){
gc_values_head_index = i;
gc_step_size_auto_mult *= 1.01f;
gc_step_size = (int)((float)values.count * gc_step_size_mult * gc_step_size_auto_mult * 2);
return OS_GC_PHASE_SWEEP;
}
gc_values_head_index = -1;
gc_start_next_values = (int)((float)values.count * gc_start_values_mult);
int end_allocated_bytes = allocator->getAllocatedBytes();
gc_continuous_count++;
if(gc_start_allocated_bytes == end_allocated_bytes){
gc_step_size_auto_mult *= 0.5f;
if(gc_step_size_auto_mult < 1){
gc_step_size_auto_mult = 1.0f;
}
if(++gc_keep_heap_count >= 2){
gc_continuous = false;
// gc_step_size_auto_mult = 1.0f;
}
}else{
gc_start_allocated_bytes = end_allocated_bytes;
gc_keep_heap_count = 0;
}
if((!gc_continuous || !(gc_continuous_count%16)) && gc_max_allocated_bytes < end_allocated_bytes){
gc_max_allocated_bytes = end_allocated_bytes;
// allocator->printf("[GC] max allocated bytes %d, values %d\n", gc_max_allocated_bytes, values.count);
}
return OS_GC_PHASE_MARK;
}
if(!gc_grey_root_initialized){
gc_grey_root_initialized = true;
gc_step_size = (int)((float)values.count * gc_step_size_mult * gc_step_size_auto_mult * 2);
gc_time++;
if(!gc_continuous){
gc_continuous = true;
gc_continuous_count = 0;
gc_keep_heap_count = 0;
// gc_start_allocated_bytes = allocator->getAllocatedBytes();
gc_step_size_auto_mult = 1.0f;
}else{
// int i = 0;
}
// int old_count = gc_grey_added_count;
gcAddToGreyList(check_recursion);
gcAddToGreyList(global_vars);
gcAddToGreyList(user_pool);
int i;
for(i = 0; i < OS_ERROR_LEVELS; i++){
gcAddToGreyList(error_handlers[i]);
}
for(i = 0; i < PROTOTYPE_COUNT; i++){
gcAddToGreyList(prototypes[i]);
}
// gcMarkTable(string_values_table);
// step_size -= gc_grey_added_count - old_count;
}
int i;
for(i = 0; i < stack_values.count; i++){
gcAddToGreyList(stack_values[i]);
}
for(i = 0; i < call_stack_funcs.count; i++){
gcMarkStackFunction(&call_stack_funcs[i]);
}
gcMarkList(step_size);
gc_step_size = (int)((float)values.count * gc_step_size_mult * gc_step_size_auto_mult * 2);
if(!gc_grey_list_first){
gc_grey_root_initialized = false;
gc_values_head_index = 0;
gc_step_size_auto_mult *= 0.25f;
if(gc_step_size_auto_mult < 1.0f){
gc_step_size_auto_mult = 1.0f;
}
return OS_GC_PHASE_SWEEP;
}
gc_step_size_auto_mult *= 1.01f;
return OS_GC_PHASE_MARK;
}
void OS::Core::gcFinishSweepPhase()
{
if(gc_in_process || values.count == 0){
return;
}
if(gc_values_head_index >= 0){
gc_step_size = values.count * 2;
gcStep();
OS_ASSERT(gc_values_head_index < 0);
}
}
void OS::Core::gcFinishMarkPhase()
{
if(gc_in_process || values.count == 0){
return;
}
while(gc_values_head_index < 0){
gc_step_size = values.count * 2;
gcStep();
}
}
void OS::Core::gcStepIfNeeded()
{
if(gc_in_process){
return;
}
if(gc_values_head_index >= 0 || gc_grey_root_initialized || gc_continuous){
gcStep();
}else if(gc_start_next_values <= values.count){
gcFinishSweepPhase();
gcStep();
}
}
void OS::Core::gcFull()
{
if(gc_in_process){
return;
}
gcFinishSweepPhase();
int start_allocated_bytes = allocator->getAllocatedBytes();
for(int i = 1;; i++){
gcFinishMarkPhase();
gcFinishSweepPhase();
int end_allocated_bytes = allocator->getAllocatedBytes();
if(start_allocated_bytes == end_allocated_bytes && i > 1){
return;
}
start_allocated_bytes = end_allocated_bytes;
}
}
/*
void OS::Core::clearValue(Value& val)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
return;
case OS_VALUE_TYPE_BOOL:
val.v.boolean = 0;
break;
case OS_VALUE_TYPE_NUMBER:
val.v.number = 0;
break;
case OS_VALUE_TYPE_WEAKREF:
val.v.value_id = 0;
break;
default:
val.v.value = 0;
}
val.type = OS_VALUE_TYPE_NULL;
}
*/
void OS::Core::clearValue(GCValue * val)
{
switch(val->type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
default:
OS_ASSERT(false);
break;
case OS_VALUE_TYPE_STRING:
{
OS_ASSERT(dynamic_cast<GCStringValue*>(val));
break;
}
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
OS_ASSERT(dynamic_cast<GCUserdataValue*>(val));
GCUserdataValue * userdata = (GCUserdataValue*)val;
void * ptr = userdata->ptr;
OS_UserdataDtor dtor = userdata->dtor;
// prevent recursion
userdata->ptr = NULL;
userdata->crc = 0;
userdata->dtor = NULL;
if(val->type == OS_VALUE_TYPE_USERPTR){
unregisterUserptrRef(ptr, userdata->value_id);
}
if(dtor){
dtor(allocator, ptr, userdata->user_param);
}
break;
}
case OS_VALUE_TYPE_FUNCTION:
{
OS_ASSERT(dynamic_cast<GCFunctionValue*>(val));
GCFunctionValue * func_value = (GCFunctionValue*)val;
clearFunctionValue(func_value);
break;
}
case OS_VALUE_TYPE_CFUNCTION:
{
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(val));
GCCFunctionValue * func_value = (GCCFunctionValue*)val;
func_value->func = NULL;
func_value->user_param = NULL;
func_value->num_closure_values = 0;
break;
}
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<GCArrayValue*>(val));
GCArrayValue * arr = (GCArrayValue*)val;
allocator->vectorClear(arr->values);
break;
}
case OS_VALUE_TYPE_OBJECT:
OS_ASSERT(dynamic_cast<GCObjectValue*>(val));
break;
}
if(val->table){
// when object is destroying, some properties could be already destroyed
// so destructor can't use self properties and can break gc process
// so destructors are disabled
/* {
bool prototype_enabled = true;
Value * func = getPropertyValue(val, PropertyIndex(strings->__destruct, PropertyIndex::KeepStringIndex()), prototype_enabled);
if(func){
pushValue(func);
pushValue(val);
call(0, 0);
}
} */
Table * table = val->table;
val->table = NULL;
deleteTable(table);
}
if(val->prototype){
// prototype could be already destroyed by gc or will be destroyed soon
val->prototype = NULL;
}
val->type = OS_VALUE_TYPE_UNKNOWN;
}
#ifdef OS_DEBUG
bool OS::Core::isValueUsed(GCValue * val)
{
struct Lib {
Core * core;
GCValue * val;
bool findAt(Value cur)
{
GCValue * value = cur.getGCValue();
return value && findAt(value);
}
bool findAt(Upvalues * upvalues)
{
int i;
for(i = 0; i < upvalues->num_locals; i++){
if(findAt(upvalues->locals[i])){
return true;
}
}
for(i = 0; i < upvalues->num_parents; i++){
if(findAt(upvalues->getParent(i))){
return true;
}
}
return false;
}
bool findAt(StackFunction * stack_func)
{
OS_ASSERT(stack_func->func);
if(findAt(stack_func->func)){
return true;
}
if(findAt(stack_func->self)){
return true;
}
if(stack_func->self_for_proto && findAt(stack_func->self_for_proto)){
return true;
}
if(stack_func->arguments && findAt(stack_func->arguments)){
return true;
}
if(stack_func->rest_arguments && findAt(stack_func->rest_arguments)){
return true;
}
return findAt(stack_func->locals);
}
bool findAt(Table * table)
{
OS_ASSERT(table);
Property * prop = table->first;
for(; prop; prop = prop->next){
if(findAt(prop->index)){
return true;
}
if(findAt(prop->value)){
return true;
}
}
return false;
}
bool findAt(GCValue * cur)
{
OS_ASSERT(cur != (GCValue*)0xdededede);
if(cur->gc_time == core->gc_time){
return false;
}
cur->gc_time = core->gc_time;
if(cur == val){
return true;
}
if(cur->prototype && findAt(cur->prototype)){
return true;
}
if(cur->table && findAt(cur->table)){
return true;
}
switch(cur->type){
case OS_VALUE_TYPE_STRING:
{
OS_ASSERT(dynamic_cast<GCStringValue*>(cur));
GCStringValue * string = (GCStringValue*)cur;
OS_ASSERT(!string->table);
break;
}
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<GCArrayValue*>(cur));
GCArrayValue * arr = (GCArrayValue*)cur;
for(int i = 0; i < arr->values.count; i++){
if(findAt(arr->values[i])){
return true;
}
}
break;
}
case OS_VALUE_TYPE_OBJECT:
OS_ASSERT(dynamic_cast<GCObjectValue*>(cur));
break;
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
OS_ASSERT(dynamic_cast<GCUserdataValue*>(cur));
break;
case OS_VALUE_TYPE_FUNCTION:
{
OS_ASSERT(dynamic_cast<GCFunctionValue*>(cur));
GCFunctionValue * func_value = (GCFunctionValue*)cur;
if(findAt(func_value->env)){
return true;
}
if(func_value->upvalues && findAt(func_value->upvalues)){
return true;
}
if(func_value->name && findAt(func_value->name)){
return true;
}
for(int i = 0; i < func_value->prog->num_strings; i++){
if(findAt(func_value->prog->const_strings[i])){
return true;
}
}
break;
}
case OS_VALUE_TYPE_CFUNCTION:
{
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(cur));
GCCFunctionValue * func_value = (GCCFunctionValue*)cur;
Value * closure_values = (Value*)(func_value + 1);
for(int i = 0; i < func_value->num_closure_values; i++){
if(findAt(closure_values[i])){
return true;
}
}
if(func_value->name && findAt(func_value->name)){
return true;
}
break;
}
case OS_VALUE_TYPE_WEAKREF:
break;
default:
OS_ASSERT(false);
}
return false;
}
} lib = {this, val};
if(lib.findAt(check_recursion)){
return true;
}
if(lib.findAt(global_vars)){
return true;
}
if(lib.findAt(user_pool)){
return true;
}
int i;
for(i = 0; i < PROTOTYPE_COUNT; i++){
if(lib.findAt(prototypes[i])){
return true;
}
}
for(i = 0; i < stack_values.count; i++){
if(lib.findAt(stack_values[i])){
return true;
}
}
for(i = 0; i < call_stack_funcs.count; i++){
if(lib.findAt(&call_stack_funcs[i])){
return true;
}
}
return false;
}
#endif
void OS::Core::deleteValue(GCValue * val)
{
OS_ASSERT(val);
OS_ASSERT(val->gc_color != GC_GREY);
unregisterValue(val->value_id);
clearValue(val);
val->~GCValue();
free(val);
num_destroyed_values++;
}
OS::Core::Property * OS::Core::setTableValue(Table * table, const PropertyIndex& index, Value value)
{
OS_ASSERT(table);
// TODO: correct ???
gcAddToGreyList(value);
Property * prop = table->get(index);
if(prop){
prop->value = value;
return prop;
}
prop = new (malloc(sizeof(Property) OS_DBG_FILEPOS)) Property(index);
prop->value = value;
addTableProperty(table, prop);
return prop;
}
bool OS::Core::hasSpecialPrefix(const Value& value)
{
if(value.type != OS_VALUE_TYPE_STRING){
return false;
}
OS_ASSERT(dynamic_cast<GCStringValue*>(value.v.string));
GCStringValue * string = value.v.string;
if(string->getLen() >= 2){
const OS_CHAR * s = string->toChar();
return s[0] == OS_TEXT('_') && s[1] == OS_TEXT('_');
}
return false;
}
void OS::Core::setPropertyValue(GCValue * table_value, const PropertyIndex& index, Value value, bool anonymous_setter_enabled, bool named_setter_enabled)
{
#if defined OS_DEBUG && defined OS_WARN_NULL_INDEX
if(table_value != check_recursion && index.index.type == OS_VALUE_TYPE_NULL){
error(OS_E_WARNING, OS_TEXT("object set null index"));
}
#endif
// TODO: correct ???
gcAddToGreyList(value);
if(index.index.type == OS_VALUE_TYPE_STRING){
OS_ASSERT(dynamic_cast<GCStringValue*>(index.index.v.string));
switch(value.type){
case OS_VALUE_TYPE_FUNCTION:
OS_ASSERT(dynamic_cast<GCFunctionValue*>(value.v.func));
if(!value.v.func->name){
value.v.func->name = index.index.v.string;
}
break;
case OS_VALUE_TYPE_CFUNCTION:
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(value.v.cfunc));
if(!value.v.cfunc->name){
value.v.cfunc->name = index.index.v.string;
}
break;
}
}
Property * prop = NULL;
Table * table = table_value->table;
if(table && (prop = table->get(index))){
prop->value = value;
return;
}
// prototype should not be used in set
/* if(prototype_enabled){
GCValue * cur_value = table_value;
while(cur_value->prototype){
cur_value = cur_value->prototype;
Table * cur_table = cur_value->table;
if(cur_table && (prop = cur_table->get(index))){
prop->value = value;
return;
}
}
} */
if(index.index.type == OS_VALUE_TYPE_STRING && strings->syntax_prototype == index.index.v.string){
switch(table_value->type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
table_value->prototype = value.v.value;
break;
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_CFUNCTION:
// TODO: warning???
break;
}
return;
}
if(table_value->type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<GCArrayValue*>(table_value));
GCArrayValue * arr = (GCArrayValue*)table_value;
int i = (int)valueToInt(index.index);
if(i < 0) i += arr->values.count;
if(i >= 0){
while(i >= arr->values.count){
allocator->vectorAddItem(arr->values, Value() OS_DBG_FILEPOS);
}
OS_ASSERT(i < arr->values.count);
arr->values[i] = value;
}
return;
}
if((anonymous_setter_enabled || named_setter_enabled) && !hasSpecialPrefix(index.index)){
Value func;
if(index.index.type == OS_VALUE_TYPE_STRING && named_setter_enabled){
const void * buf1 = strings->__setAt.toChar();
int size1 = strings->__setAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * setter_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(func, table_value, PropertyIndex(setter_name, PropertyIndex::KeepStringIndex()), true)){
pushValue(func);
pushValue(table_value);
pushValue(value);
call(1, 0);
return;
}
}
if(anonymous_setter_enabled && getPropertyValue(func, table_value, PropertyIndex(strings->__set, PropertyIndex::KeepStringIndex()), true)){
pushValue(func);
pushValue(table_value);
pushValue(index.index);
pushValue(value);
call(2, 0);
return;
}
}
if(table_value->type == OS_VALUE_TYPE_STRING){
// TODO: trigger error???
return;
}
if(!table){
table_value->table = table = newTable(OS_DBG_FILEPOS_START);
}
prop = new (malloc(sizeof(Property) OS_DBG_FILEPOS)) Property(index);
prop->value = value;
addTableProperty(table, prop);
// setTableValue(table, index, value);
}
void OS::Core::setPropertyValue(Value table_value, const PropertyIndex& index, Value value, bool anonymous_setter_enabled, bool named_setter_enabled)
{
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
return;
case OS_VALUE_TYPE_BOOL:
// return setPropertyValue(prototypes[PROTOTYPE_BOOL], index, value, setter_enabled);
return;
case OS_VALUE_TYPE_NUMBER:
// return setPropertyValue(prototypes[PROTOTYPE_NUMBER], index, value, setter_enabled);
return;
case OS_VALUE_TYPE_STRING:
// return setPropertyValue(prototypes[PROTOTYPE_STRING], index, value, setter_enabled);
// return;
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return setPropertyValue(table_value.v.value, index, value, anonymous_setter_enabled, named_setter_enabled);
}
}
void OS::Core::pushPrototype(Value val)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
pushNull();
return;
case OS_VALUE_TYPE_BOOL:
pushValue(prototypes[PROTOTYPE_BOOL]);
return;
case OS_VALUE_TYPE_NUMBER:
pushValue(prototypes[PROTOTYPE_NUMBER]);
return;
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
pushValue(val.v.value);
return;
}
}
void OS::Core::setPrototype(Value val, Value proto, int userdata_crc)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
return;
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
if(val.v.userdata->crc != userdata_crc){
return;
}
// no break
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
val.v.value->prototype = proto.getGCValue();
return;
}
}
OS::Core::GCStringValue * OS::Core::newStringValue(const OS_CHAR * str)
{
return newStringValue(str, OS_STRLEN(str));
}
OS::Core::GCStringValue * OS::Core::newStringValue(const OS_CHAR * str, int len)
{
return newStringValue((void*)str, len * sizeof(OS_CHAR));
}
OS::Core::GCStringValue * OS::Core::newStringValue(const OS_CHAR * str, int len, const OS_CHAR * str2, int len2)
{
return newStringValue((void*)str, len * sizeof(OS_CHAR), str2, len2 * sizeof(OS_CHAR));
}
OS::Core::GCStringValue * OS::Core::newStringValue(const OS_CHAR * str, int len, bool trim_left, bool trim_right)
{
if(trim_left){
while(len > 0 && OS_IS_SPACE(*str)){
str++;
len--;
}
}
if(trim_right){
while(len > 0 && OS_IS_SPACE(str[len-1])){
len--;
}
}
return newStringValue((void*)str, len * sizeof(OS_CHAR));
}
OS::Core::GCStringValue * OS::Core::newStringValue(const String& p_str, bool trim_left, bool trim_right)
{
const OS_CHAR * str = p_str.toChar();
int len = p_str.getLen();
bool changed = false;
if(trim_left){
while(len > 0 && OS_IS_SPACE(*str)){
str++;
len--;
changed = true;
}
}
if(trim_right){
while(len > 0 && OS_IS_SPACE(str[len-1])){
len--;
changed = true;
}
}
if(changed){
return newStringValue(str, len);
}
return p_str.string;
}
OS::Core::GCStringValue * OS::Core::newStringValue(const String& str)
{
return str.string;
}
OS::Core::GCStringValue * OS::Core::newStringValue(const void * buf, int size)
{
return newStringValue(buf, size, NULL, 0);
}
OS::Core::GCStringValue * OS::Core::newStringValue(const void * buf1, int size1, const void * buf2, int size2)
{
if(string_refs.count > 0){
OS_ASSERT(string_refs.heads && string_refs.head_mask);
int hash = Utils::keyToHash(buf1, size1, buf2, size2);
int slot = hash & string_refs.head_mask;
StringRef * str_ref = string_refs.heads[slot];
for(StringRef * prev = NULL, * next; str_ref; str_ref = next){
next = str_ref->hash_next;
GCStringValue * string_value = (GCStringValue*)values.get(str_ref->string_value_id);
if(!string_value){
if(!prev){
string_refs.heads[slot] = next;
}else{
prev->hash_next = next;
}
free(str_ref);
string_refs.count--;
continue;
}
OS_ASSERT(string_value->type == OS_VALUE_TYPE_STRING);
OS_ASSERT(dynamic_cast<GCStringValue*>(string_value));
if(string_value->isEqual(hash, buf1, size1, buf2, size2)){
return string_value;
}
prev = str_ref;
}
}
GCStringValue * string_value = GCStringValue::alloc(allocator, buf1, size1, buf2, size2 OS_DBG_FILEPOS);
StringRef * str_ref = (StringRef*)malloc(sizeof(StringRef) OS_DBG_FILEPOS);
str_ref->string_hash = string_value->hash;
str_ref->string_value_id = string_value->value_id;
str_ref->hash_next = NULL;
registerStringRef(str_ref);
return string_value;
}
OS::Core::GCStringValue * OS::Core::newStringValue(const void * buf1, int size1, const void * buf2, int size2, const void * buf3, int size3)
{
if(size1 <= 0){
return newStringValue(buf2, size2, buf3, size3);
}
if(size2 <= 0){
return newStringValue(buf1, size1, buf3, size3);
}
if(size3 <= 0){
return newStringValue(buf1, size1, buf2, size2);
}
if(size1 + size2 + size3 <= 1024){
OS_BYTE * buf = (OS_BYTE*)alloca(size1 + size2 + size3 + sizeof(OS_CHAR));
OS_MEMCPY(buf, buf1, size1);
OS_MEMCPY(buf+size1, buf2, size2);
OS_MEMCPY(buf+size1+size2, buf3, size3);
buf[size1+size2+size3] = (OS_CHAR)0;
return newStringValue(buf, (size1 + size2 + size3) / sizeof(OS_CHAR));
}
GCStringValue * str = newStringValue(buf1, size1, buf2, size2);
return newStringValue(str->toBytes(), str->data_size, buf3, size3);
}
OS::Core::GCStringValue * OS::Core::newStringValue(GCStringValue * a, GCStringValue * b)
{
if(a->data_size <= 0){
return b;
}
if(b->data_size <= 0){
return a;
}
return newStringValue((void*)a->toBytes(), a->data_size, (void*)b->toBytes(), b->data_size);
}
OS::Core::GCStringValue * OS::Core::newStringValue(const String& a, const String& b)
{
return newStringValue(a.string, b.string);
}
OS::Core::GCStringValue * OS::Core::newStringValue(OS_INT val)
{
OS_CHAR str[128];
Utils::numToStr(str, val);
return newStringValue(str);
}
OS::Core::GCStringValue * OS::Core::newStringValue(OS_FLOAT val, int precision)
{
OS_CHAR str[128];
Utils::numToStr(str, val, precision);
return newStringValue(str);
}
OS::Core::GCStringValue * OS::Core::newStringValue(int temp_buf_len, const OS_CHAR * fmt, ...)
{
va_list va;
va_start(va, fmt);
OS_VaListDtor va_dtor(&va);
return newStringValueVa(temp_buf_len, fmt, va);
}
OS::Core::GCStringValue * OS::Core::newStringValueVa(int temp_buf_len, const OS_CHAR * fmt, va_list va)
{
OS_ASSERT(temp_buf_len <= OS_DEF_FMT_BUF_LEN);
OS_CHAR * buf = (OS_CHAR*)malloc(temp_buf_len * sizeof(OS_CHAR) OS_DBG_FILEPOS);
OS_VSNPRINTF(buf, sizeof(OS_CHAR) * (temp_buf_len-1), fmt, va);
GCStringValue * result = newStringValue(buf);
free(buf);
return result;
}
OS::Core::GCCFunctionValue * OS::Core::newCFunctionValue(OS_CFunction func, void * user_param)
{
return newCFunctionValue(func, 0, user_param);
}
OS::Core::GCCFunctionValue * OS::Core::newCFunctionValue(OS_CFunction func, int num_closure_values, void * user_param)
{
OS_ASSERT(stack_values.count >= num_closure_values);
if(!func){
return NULL;
}
GCCFunctionValue * res = new (malloc(sizeof(GCCFunctionValue) + sizeof(Value) * num_closure_values OS_DBG_FILEPOS)) GCCFunctionValue();
res->prototype = prototypes[PROTOTYPE_FUNCTION];
res->name = NULL;
res->func = func;
res->user_param = user_param;
res->num_closure_values = num_closure_values;
Value * closure_values = (Value*)(res + 1);
if(num_closure_values > 0){
OS_MEMCPY(closure_values, stack_values.buf + (stack_values.count - num_closure_values), sizeof(Value)*num_closure_values);
}
res->type = OS_VALUE_TYPE_CFUNCTION;
pop(num_closure_values);
registerValue(res);
return res;
}
OS::Core::GCUserdataValue * OS::Core::newUserdataValue(int crc, int data_size, OS_UserdataDtor dtor, void * user_param)
{
GCUserdataValue * res = new (malloc(sizeof(GCUserdataValue) + data_size OS_DBG_FILEPOS)) GCUserdataValue();
res->prototype = prototypes[PROTOTYPE_USERDATA];
res->crc = crc;
res->dtor = dtor;
res->user_param = user_param;
res->ptr = data_size ? res + 1 : NULL;
res->type = OS_VALUE_TYPE_USERDATA;
registerValue(res);
return res;
}
OS::Core::GCUserdataValue * OS::Core::newUserPointerValue(int crc, void * ptr, OS_UserdataDtor dtor, void * user_param)
{
int hash = (int)(intptr_t)ptr;
if(userptr_refs.count > 0){
OS_ASSERT(userptr_refs.heads && userptr_refs.head_mask);
int slot = hash & userptr_refs.head_mask;
UserptrRef * userptr_ref = userptr_refs.heads[slot];
for(UserptrRef * prev = NULL, * next; userptr_ref; userptr_ref = next){
next = userptr_ref->hash_next;
GCUserdataValue * userptr_value = (GCUserdataValue*)values.get(userptr_ref->userptr_value_id);
if(!userptr_value){
if(!prev){
userptr_refs.heads[slot] = next;
}else{
prev->hash_next = next;
}
free(userptr_ref);
userptr_refs.count--;
continue;
}
OS_ASSERT(userptr_value->type == OS_VALUE_TYPE_USERPTR);
OS_ASSERT(dynamic_cast<GCUserdataValue*>(userptr_value));
if(userptr_value->ptr == ptr){ // && userptr_value->crc == crc){
OS_ASSERT(userptr_value->crc == crc);
if(userptr_value->crc != crc){
if(!prev){
userptr_refs.heads[slot] = next;
}else{
prev->hash_next = next;
}
free(userptr_ref);
userptr_refs.count--;
continue;
}
return userptr_value;
}
prev = userptr_ref;
}
}
GCUserdataValue * res = new (malloc(sizeof(GCUserdataValue) OS_DBG_FILEPOS)) GCUserdataValue();
res->prototype = prototypes[PROTOTYPE_USERDATA];
res->crc = crc;
res->dtor = dtor;
res->user_param = user_param;
res->ptr = ptr;
res->type = OS_VALUE_TYPE_USERPTR;
registerValue(res);
UserptrRef * userptr_ref = (UserptrRef*)malloc(sizeof(UserptrRef) OS_DBG_FILEPOS);
userptr_ref->userptr_hash = hash;
userptr_ref->userptr_value_id = res->value_id;
userptr_ref->hash_next = NULL;
registerUserptrRef(userptr_ref);
return res;
}
OS::Core::GCObjectValue * OS::Core::newObjectValue()
{
return newObjectValue(prototypes[PROTOTYPE_OBJECT]);
}
OS::Core::GCObjectValue * OS::Core::newObjectValue(GCValue * prototype)
{
GCObjectValue * res = new (malloc(sizeof(GCObjectValue) OS_DBG_FILEPOS)) GCObjectValue();
res->prototype = prototype;
res->type = OS_VALUE_TYPE_OBJECT;
registerValue(res);
return res;
}
OS::Core::GCArrayValue * OS::Core::newArrayValue(int initial_capacity)
{
GCArrayValue * res = new (malloc(sizeof(GCArrayValue) OS_DBG_FILEPOS)) GCArrayValue();
res->prototype = prototypes[PROTOTYPE_ARRAY];
res->type = OS_VALUE_TYPE_ARRAY;
if(initial_capacity > 0){
allocator->vectorReserveCapacity(res->values, initial_capacity OS_DBG_FILEPOS);
}
registerValue(res);
return res;
}
void OS::Core::pushValue(const Value& p_val)
{
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
Value val = p_val;
reserveStackValues(stack_values.count+1);
stack_values.buf[stack_values.count++] = val;
}else{
stack_values.buf[stack_values.count++] = p_val;
}
}
void OS::Core::pushNull()
{
pushValue(Value());
}
void OS::Core::pushStackValue(int offs)
{
pushValue(getStackValue(offs));
}
void OS::Core::copyValue(int raw_from, int raw_to)
{
reserveStackValues(raw_to+1);
stack_values.buf[raw_to] = stack_values.buf[raw_from];
}
/*
void OS::Core::pushTrue()
{
pushValue(true);
}
void OS::Core::pushFalse()
{
pushValue(false);
}
*/
void OS::Core::pushBool(bool val)
{
#if 1 // speed optimization
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
reserveStackValues(stack_values.count+1);
}
stack_values.buf[stack_values.count++] = val;
#else
pushValue(val);
#endif
}
void OS::Core::pushNumber(OS_INT32 val)
{
#if 1 // speed optimization
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
reserveStackValues(stack_values.count+1);
}
stack_values.buf[stack_values.count++] = val;
#else
pushValue(val);
#endif
}
void OS::Core::pushNumber(OS_INT64 val)
{
#if 1 // speed optimization
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
reserveStackValues(stack_values.count+1);
}
stack_values.buf[stack_values.count++] = val;
#else
pushValue(val);
#endif
}
void OS::Core::pushNumber(float val)
{
#if 1 // speed optimization
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
reserveStackValues(stack_values.count+1);
}
stack_values.buf[stack_values.count++] = val;
#else
pushValue(val);
#endif
}
void OS::Core::pushNumber(double val)
{
#if 1 // speed optimization
StackValues& stack_values = this->stack_values;
if(stack_values.capacity < stack_values.count+1){
reserveStackValues(stack_values.count+1);
}
stack_values.buf[stack_values.count++] = val;
#else
pushValue(val);
#endif
}
OS::Core::GCStringValue * OS::Core::pushStringValue(const String& val)
{
return pushValue(newStringValue(val));
}
OS::Core::GCStringValue * OS::Core::pushStringValue(const OS_CHAR * val)
{
return pushValue(newStringValue(val));
}
OS::Core::GCStringValue * OS::Core::pushStringValue(const OS_CHAR * val, int len)
{
return pushValue(newStringValue(val, len));
}
OS::Core::GCCFunctionValue * OS::Core::pushCFunctionValue(OS_CFunction func, void * user_param)
{
return pushValue(newCFunctionValue(func, user_param));
}
OS::Core::GCCFunctionValue * OS::Core::pushCFunctionValue(OS_CFunction func, int closure_values, void * user_param)
{
return pushValue(newCFunctionValue(func, closure_values, user_param));
}
OS::Core::GCUserdataValue * OS::Core::pushUserdataValue(int crc, int data_size, OS_UserdataDtor dtor, void * user_param)
{
return pushValue(newUserdataValue(crc, data_size, dtor, user_param));
}
OS::Core::GCUserdataValue * OS::Core::pushUserPointerValue(int crc, void * data, OS_UserdataDtor dtor, void * user_param)
{
return pushValue(newUserPointerValue(crc, data, dtor, user_param));
}
OS::Core::GCObjectValue * OS::Core::pushObjectValue()
{
return pushValue(newObjectValue());
}
OS::Core::GCObjectValue * OS::Core::pushObjectValue(GCValue * prototype)
{
return pushValue(newObjectValue(prototype));
}
OS::Core::GCArrayValue * OS::Core::pushArrayValue(int initial_capacity)
{
return pushValue(newArrayValue(initial_capacity));
}
void OS::Core::pushTypeOf(Value val)
{
switch(val.type){
// case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_BOOL:
pushStringValue(strings->typeof_boolean);
return;
case OS_VALUE_TYPE_NUMBER:
pushStringValue(strings->typeof_number);
return;
case OS_VALUE_TYPE_STRING:
pushStringValue(strings->typeof_string);
return;
case OS_VALUE_TYPE_ARRAY:
pushStringValue(strings->typeof_array);
return;
case OS_VALUE_TYPE_OBJECT:
pushStringValue(strings->typeof_object);
return;
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
pushStringValue(strings->typeof_userdata);
return;
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
pushStringValue(strings->typeof_function);
return;
}
pushStringValue(strings->typeof_null);
}
bool OS::Core::pushNumberOf(Value val)
{
if(val.type == OS_VALUE_TYPE_NUMBER){
pushValue(val);
return true;
}
OS_NUMBER number;
if(isValueNumber(val, &number)){
pushNumber(number);
return true;
}
pushNull();
return false;
}
bool OS::Core::pushStringOf(Value val)
{
if(val.type == OS_VALUE_TYPE_STRING){
pushValue(val);
return true;
}
String str(allocator);
if(isValueString(val, &str)){
pushStringValue(str);
return true;
}
pushNull();
return false;
}
bool OS::Core::pushValueOf(Value val)
{
switch(val.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
pushValue(val);
return true;
}
OS_ASSERT(check_recursion && check_recursion->type == OS_VALUE_TYPE_OBJECT);
if(++check_recursion->external_ref_count == 1 && check_recursion->table){
clearTable(check_recursion->table);
}
setPropertyValue(check_recursion, val, Value(true), false, false);
struct Finalizer {
Core * core;
~Finalizer()
{
if(--core->check_recursion->external_ref_count == 0 && core->check_recursion->table){
core->clearTable(core->check_recursion->table);
}
if(core->check_recursion->gc_color == GC_WHITE){
core->check_recursion->gc_color = GC_BLACK;
}
}
} finalizer = {this};
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, val.v.value, PropertyIndex(strings->__valueof, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
pushValue(func);
pushValue(val);
call(0, 1);
switch(stack_values.lastElement().type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
return true;
}
// TODO: warning
pop();
}
pushNull();
return false;
}
OS::Core::GCArrayValue * OS::Core::pushArrayOf(Value val)
{
// GCArrayValue * arr;
switch(val.type){
// case OS_VALUE_TYPE_NULL:
// return pushNull(); // pushArrayValue();
/*
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_STRING:
arr = pushArrayValue();
allocator->vectorAddItem(arr->values, val OS_DBG_FILEPOS);
return arr;
*/
case OS_VALUE_TYPE_ARRAY:
return pushValue(val.v.arr);
/*
case OS_VALUE_TYPE_OBJECT:
arr = pushArrayValue();
if(val.v.object->table && val.v.object->table->count > 0){
Property * prop = val.v.object->table->first;
for(; prop; prop = prop->next){
allocator->vectorAddItem(arr->values, prop->value OS_DBG_FILEPOS);
}
}
return arr;
*/
}
pushNull();
return NULL;
}
OS::Core::GCObjectValue * OS::Core::pushObjectOf(Value val)
{
// GCObjectValue * object;
switch(val.type){
// case OS_VALUE_TYPE_NULL:
// return pushObjectValue();
/*
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_STRING:
object = pushObjectValue();
setPropertyValue(object, Value(0), val, false);
return object;
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<GCArrayValue*>(val.v.arr));
object = pushObjectValue();
GCArrayValue * arr = (GCArrayValue*)val.v.arr;
for(int i = 0; i < arr->values.count; i++){
setPropertyValue(object, Value(i), arr->values[i], false);
}
return object;
}
*/
case OS_VALUE_TYPE_OBJECT:
return pushValue(val.v.object);
}
pushNull();
return NULL;
}
OS::Core::GCUserdataValue * OS::Core::pushUserdataOf(Value val)
{
switch(val.type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return pushValue(val.v.userdata);
}
pushNull();
return NULL;
}
bool OS::Core::pushFunctionOf(Value val)
{
switch(val.type){
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
pushValue(val);
return true;
}
pushNull();
return false;
}
void OS::Core::pushCloneValue(Value val)
{
GCValue * value, * new_value;
switch(val.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_STRING:
pushValue(val);
return;
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<GCArrayValue*>(val.v.value));
value = val.v.value;
new_value = pushArrayValue();
new_value->prototype = value->prototype;
GCArrayValue * arr = (GCArrayValue*)value;
GCArrayValue * new_arr = (GCArrayValue*)new_value;
allocator->vectorReserveCapacity(new_arr->values, arr->values.count OS_DBG_FILEPOS);
for(int i = 0; i < arr->values.count; i++){
allocator->vectorAddItem(new_arr->values, arr->values[i] OS_DBG_FILEPOS);
}
break;
}
case OS_VALUE_TYPE_OBJECT:
value = val.v.value;
new_value = pushObjectValue(value->prototype);
break;
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_CFUNCTION:
value = val.v.value;
new_value = pushValue(value);
break;
default:
pushNull();
return;
}
OS_ASSERT(new_value->type != OS_VALUE_TYPE_NULL);
if(new_value != value && value->table && value->table->count > 0){
new_value->table = newTable(OS_DBG_FILEPOS_START);
copyTableProperties(new_value->table, value->table);
}
// removeStackValue(-2);
switch(new_value->type){
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, new_value,
PropertyIndex(strings->__clone, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
pushValue(func);
pushValue(new_value);
call(0, 1);
OS_ASSERT(stack_values.count >= 2);
removeStackValue(-2);
}
}
}
}
void OS::Core::pushOpResultValue(int opcode, Value value)
{
struct Lib
{
Core * core;
void pushSimpleOpcodeValue(int opcode, Value value)
{
switch(opcode){
case Program::OP_BIT_NOT:
return core->pushNumber(~core->valueToInt(value));
case Program::OP_PLUS:
if(value.type == OS_VALUE_TYPE_NUMBER){
return core->pushValue(value);
}
return core->pushNumber(core->valueToNumber(value));
case Program::OP_NEG:
return core->pushNumber(-core->valueToNumber(value));
case Program::OP_LENGTH:
// return core->pushNumber(core->valueToString(value).getDataSize() / sizeof(OS_CHAR));
return pushObjectMethodOpcodeValue(core->strings->__len, value);
}
return core->pushNull();
}
void pushObjectMethodOpcodeValue(const String& method_name, Value value)
{
bool prototype_enabled = true;
Value func;
if(core->getPropertyValue(func, value,
PropertyIndex(method_name, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
core->pushValue(func);
core->pushValue(value);
core->call(0, 1);
return;
}
return core->pushNull();
}
void pushObjectOpcodeValue(int opcode, Value value)
{
switch(opcode){
case Program::OP_BIT_NOT:
return pushObjectMethodOpcodeValue(core->strings->__bitnot, value);
case Program::OP_PLUS:
return pushObjectMethodOpcodeValue(core->strings->__plus, value);
case Program::OP_NEG:
return pushObjectMethodOpcodeValue(core->strings->__neg, value);
case Program::OP_LENGTH:
return pushObjectMethodOpcodeValue(core->strings->__len, value);
}
return core->pushNull();
}
void pushUnaryOpcodeValue(int opcode, Value value)
{
switch(value.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
return pushSimpleOpcodeValue(opcode, value);
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return pushObjectOpcodeValue(opcode, value);
}
return core->pushNull();
}
} lib = {this};
return lib.pushUnaryOpcodeValue(opcode, value);
}
void OS::Core::pushOpResultValue(int opcode, Value left_value, Value right_value)
{
struct Lib
{
Core * core;
bool isEqualExactly(Value left_value, Value right_value)
{
if(left_value.type == right_value.type){ // && left_value->prototype == right_value->prototype){
switch(left_value.type){
case OS_VALUE_TYPE_NULL:
return true;
case OS_VALUE_TYPE_NUMBER:
return left_value.v.number == right_value.v.number;
case OS_VALUE_TYPE_BOOL:
return left_value.v.boolean == right_value.v.boolean;
case OS_VALUE_TYPE_STRING:
// the same strings are always share one instance, so check only value_id
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return left_value.v.value == right_value.v.value;
case OS_VALUE_TYPE_WEAKREF:
return left_value.v.value_id == right_value.v.value_id;
}
}
return false;
}
int compareNumbers(OS_NUMBER num1, OS_NUMBER num2)
{
if(num1 > num2){
return 1;
}
if(num1 < num2){
return -1;
}
return 0;
}
int compareStrings(GCStringValue * left_string_data, OS_NUMBER right_number)
{
OS_CHAR buf[128];
Utils::numToStr(buf, right_number);
return left_string_data->cmp(buf);
}
int compareStrings(GCStringValue * left_string_data, GCStringValue * right_string_data)
{
return left_string_data->cmp(right_string_data);
}
int compareObjectToValue(Value left_value, Value right_value)
{
GCValue * left = left_value.v.value;
switch(left->type){
case OS_VALUE_TYPE_STRING:
{
OS_ASSERT(dynamic_cast<GCStringValue*>(left));
GCStringValue * string = (GCStringValue*)left;
return compareStringToValue(left_value, string, right_value);
}
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
return 1;
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
bool prototype_enabled = true;
Value func;
if(core->getPropertyValue(func, left,
PropertyIndex(core->strings->__cmp, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
core->pushValue(func);
core->pushValue(left);
core->pushValue(right_value);
core->call(1, 1);
OS_ASSERT(core->stack_values.count >= 1);
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {core};
Value value = core->stack_values.lastElement();
if(value.type == OS_VALUE_TYPE_NUMBER){
return (int)value.v.number;
}
}
if(right_value.type != OS_VALUE_TYPE_STRING && right_value.type != OS_VALUE_TYPE_NUMBER && right_value.type != OS_VALUE_TYPE_BOOL){
GCValue * right = right_value.v.value;
OS_ASSERT(right->type == right_value.type);
if(left->prototype != right->prototype){
switch(right->type){
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
if(core->getPropertyValue(func, right,
PropertyIndex(core->strings->__cmp, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
core->pushValue(func);
core->pushValue(right_value);
core->pushValue(left);
core->call(1, 1);
OS_ASSERT(core->stack_values.count >= 1);
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {core};
Value value = core->stack_values.lastElement();
if(value.type == OS_VALUE_TYPE_NUMBER){
return -(int)value.v.number;
}
}
}
}
}
core->pushValueOf(Value(left));
Value left_value = core->stack_values.lastElement();
struct Pop { Core * core; ~Pop(){ core->pop(); } } pop = {core};
return compareValues(left_value, right_value);
}
}
break;
}
// generic compare
return 1; // left->value_id - (int)right_value;
}
int compareNumberToValue(Value left_value, OS_NUMBER left_number, Value right_value)
{
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
return 1;
case OS_VALUE_TYPE_NUMBER:
return compareNumbers(left_number, right_value.v.number);
case OS_VALUE_TYPE_BOOL:
return compareNumbers(left_number, (OS_NUMBER)right_value.v.boolean);
case OS_VALUE_TYPE_STRING:
return -compareStrings(right_value.v.string, left_number);
}
return -compareObjectToValue(right_value, left_value);
}
int compareStringToValue(Value left_value, GCStringValue * left_string_data, Value right_value)
{
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
return 1;
case OS_VALUE_TYPE_NUMBER:
return compareStrings(left_string_data, right_value.v.number);
case OS_VALUE_TYPE_BOOL:
return compareStrings(left_string_data, (OS_NUMBER)right_value.v.boolean);
case OS_VALUE_TYPE_STRING:
return compareStrings(left_string_data, right_value.v.string);
}
return -compareObjectToValue(right_value, left_value);
}
int compareValues(Value left_value, Value right_value)
{
switch(left_value.type){
case OS_VALUE_TYPE_NULL:
return right_value.type == OS_VALUE_TYPE_NULL ? 0 : -1;
case OS_VALUE_TYPE_NUMBER:
return compareNumberToValue(left_value, left_value.v.number, right_value);
case OS_VALUE_TYPE_BOOL:
return compareNumberToValue(left_value, (OS_NUMBER)left_value.v.boolean, right_value);
// case OS_VALUE_TYPE_STRING:
// return compareStringToValue(left_value->v.string_data, right_value);
}
return compareObjectToValue(left_value, right_value);
}
void pushSimpleOpcodeValue(int opcode, Value left_value, Value right_value)
{
switch(opcode){
case Program::OP_CONCAT:
core->pushStringValue(core->newStringValue(core->valueToString(left_value), core->valueToString(right_value)));
return;
case Program::OP_BIT_AND:
return core->pushNumber(core->valueToInt(left_value) & core->valueToInt(right_value));
case Program::OP_BIT_OR:
return core->pushNumber(core->valueToInt(left_value) | core->valueToInt(right_value));
case Program::OP_BIT_XOR:
return core->pushNumber(core->valueToInt(left_value) ^ core->valueToInt(right_value));
case Program::OP_ADD: // +
return core->pushNumber(core->valueToNumber(left_value) + core->valueToNumber(right_value));
case Program::OP_SUB: // -
return core->pushNumber(core->valueToNumber(left_value) - core->valueToNumber(right_value));
case Program::OP_MUL: // *
return core->pushNumber(core->valueToNumber(left_value) * core->valueToNumber(right_value));
case Program::OP_DIV: // /
{
OS_FLOAT right = core->valueToNumber(right_value);
if(!right){
core->errorDivisionByZero();
return core->pushNumber(0.0);
}
return core->pushNumber(core->valueToNumber(left_value) / right);
}
case Program::OP_MOD: // %
{
OS_FLOAT right = core->valueToNumber(right_value);
if(!right){
core->errorDivisionByZero();
return core->pushNumber(0.0);
}
return core->pushNumber(OS_MATH_MOD_OPERATOR(core->valueToNumber(left_value), right));
}
case Program::OP_LSHIFT: // <<
return core->pushNumber(core->valueToInt(left_value) << core->valueToInt(right_value));
case Program::OP_RSHIFT: // >>
return core->pushNumber(core->valueToInt(left_value) >> core->valueToInt(right_value));
case Program::OP_POW: // **
return core->pushNumber(OS_MATH_POW_OPERATOR(core->valueToNumber(left_value), core->valueToNumber(right_value)));
}
core->pushNull();
}
void pushObjectMethodOpcodeValue(int opcode, const String& method_name, Value left_value, Value right_value, GCValue * object, bool is_left_side)
{
bool prototype_enabled = true;
Value func;
if(core->getPropertyValue(func, object,
PropertyIndex(method_name, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
core->pushValue(func);
core->pushValue(object);
core->pushValue(left_value);
core->pushValue(right_value);
core->pushValue(is_left_side ? right_value : left_value);
core->call(3, 1);
return;
}
Value other_value = is_left_side ? right_value : left_value;
switch(other_value.type){
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
GCValue * other = other_value.v.value;
if(object->prototype == other->prototype){
core->pushNull();
return;
}
if(core->getPropertyValue(func, other,
PropertyIndex(method_name, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
core->pushValue(func);
core->pushValue(other_value);
core->pushValue(left_value);
core->pushValue(right_value);
core->pushValue(!is_left_side ? right_value : left_value);
core->call(3, 1);
return;
}
}
}
if(is_left_side){
core->pushValueOf(left_value);
pushBinaryOpcodeValue(opcode, core->stack_values.lastElement(), right_value);
core->removeStackValue(-2);
}else{
core->pushValueOf(right_value);
pushBinaryOpcodeValue(opcode, left_value, core->stack_values.lastElement());
core->removeStackValue(-2);
}
}
void pushObjectOpcodeValue(int opcode, Value left_value, Value right_value, GCValue * object, bool is_left_side)
{
switch(opcode){
case Program::OP_CONCAT:
return pushObjectMethodOpcodeValue(opcode, core->strings->__concat, left_value, right_value, object, is_left_side);
case Program::OP_BIT_AND:
return pushObjectMethodOpcodeValue(opcode, core->strings->__bitand, left_value, right_value, object, is_left_side);
case Program::OP_BIT_OR:
return pushObjectMethodOpcodeValue(opcode, core->strings->__bitor, left_value, right_value, object, is_left_side);
case Program::OP_BIT_XOR:
return pushObjectMethodOpcodeValue(opcode, core->strings->__bitxor, left_value, right_value, object, is_left_side);
case Program::OP_ADD: // +
return pushObjectMethodOpcodeValue(opcode, core->strings->__add, left_value, right_value, object, is_left_side);
case Program::OP_SUB: // -
return pushObjectMethodOpcodeValue(opcode, core->strings->__sub, left_value, right_value, object, is_left_side);
case Program::OP_MUL: // *
return pushObjectMethodOpcodeValue(opcode, core->strings->__mul, left_value, right_value, object, is_left_side);
case Program::OP_DIV: // /
return pushObjectMethodOpcodeValue(opcode, core->strings->__div, left_value, right_value, object, is_left_side);
case Program::OP_MOD: // %
return pushObjectMethodOpcodeValue(opcode, core->strings->__mod, left_value, right_value, object, is_left_side);
case Program::OP_LSHIFT: // <<
return pushObjectMethodOpcodeValue(opcode, core->strings->__lshift, left_value, right_value, object, is_left_side);
case Program::OP_RSHIFT: // >>
return pushObjectMethodOpcodeValue(opcode, core->strings->__rshift, left_value, right_value, object, is_left_side);
case Program::OP_POW: // **
return pushObjectMethodOpcodeValue(opcode, core->strings->__pow, left_value, right_value, object, is_left_side);
}
core->pushNull();
}
void pushBinaryOpcodeValue(int opcode, Value left_value, Value right_value)
{
switch(left_value.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
return pushSimpleOpcodeValue(opcode, left_value, right_value);
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return pushObjectOpcodeValue(opcode, left_value, right_value, right_value.v.value, false);
}
break;
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_STRING:
// return pushObjectOpcodeValue(opcode, left_value, right_value, left_value.v.value, true);
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return pushObjectOpcodeValue(opcode, left_value, right_value, left_value.v.value, true);
}
}
core->pushNull();
}
} lib = {this};
StackValues * stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
switch(opcode){
case Program::OP_COMPARE:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number - right_value.v.number;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value);
return;
case Program::OP_LOGIC_PTR_EQ:
stack_values->buf[stack_values->count++] = lib.isEqualExactly(left_value, right_value);
return;
case Program::OP_LOGIC_PTR_NE:
stack_values->buf[stack_values->count++] = !lib.isEqualExactly(left_value, right_value);
return;
case Program::OP_LOGIC_EQ:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number == right_value.v.number;
return;
}
if(left_value.type == OS_VALUE_TYPE_STRING && right_value.type == OS_VALUE_TYPE_STRING){
stack_values->buf[stack_values->count++] = left_value.v.string == right_value.v.string;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) == 0;
return;
case Program::OP_LOGIC_NE:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number != right_value.v.number;
return;
}
if(left_value.type == OS_VALUE_TYPE_STRING && right_value.type == OS_VALUE_TYPE_STRING){
stack_values->buf[stack_values->count++] = left_value.v.string != right_value.v.string;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) != 0;
return;
case Program::OP_LOGIC_GE:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number >= right_value.v.number;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) >= 0;
return;
case Program::OP_LOGIC_LE:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number <= right_value.v.number;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) <= 0;
return;
case Program::OP_LOGIC_GREATER:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number > right_value.v.number;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) > 0;
return;
case Program::OP_LOGIC_LESS:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number < right_value.v.number;
return;
}
stack_values->buf[stack_values->count++] = lib.compareValues(left_value, right_value) < 0;
return;
case Program::OP_CONCAT:
if(left_value.type == OS_VALUE_TYPE_STRING && right_value.type == OS_VALUE_TYPE_STRING){
pushStringValue(newStringValue(left_value.v.string, right_value.v.string));
return;
}
break;
case Program::OP_BIT_AND:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number & (OS_INT)right_value.v.number;
return;
}
break;
case Program::OP_BIT_OR:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number | (OS_INT)right_value.v.number;
return;
}
break;
case Program::OP_BIT_XOR:
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number ^ (OS_INT)right_value.v.number;
return;
}
break;
case Program::OP_ADD: // +
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number + right_value.v.number;
return;
}
break;
case Program::OP_SUB: // -
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number - right_value.v.number;
return;
}
break;
case Program::OP_MUL: // *
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = left_value.v.number * right_value.v.number;
return;
}
break;
case Program::OP_DIV: // /
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
if(!right_value.v.number){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
return;
}
stack_values->buf[stack_values->count++] = left_value.v.number / right_value.v.number;
return;
}
break;
case Program::OP_MOD: // %
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
if(!right_value.v.number){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
return;
}
stack_values->buf[stack_values->count++] = OS_MATH_MOD_OPERATOR(left_value.v.number, right_value.v.number);
return;
}
break;
case Program::OP_LSHIFT: // <<
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number << (OS_INT)right_value.v.number;
return;
}
break;
case Program::OP_RSHIFT: // >>
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number >> (OS_INT)right_value.v.number;
return;
}
break;
case Program::OP_POW: // **
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
stack_values->buf[stack_values->count++] = OS_MATH_POW_OPERATOR((OS_FLOAT)left_value.v.number, (OS_FLOAT)right_value.v.number);
return;
}
break;
}
return lib.pushBinaryOpcodeValue(opcode, left_value, right_value);
}
void OS::Core::setGlobalValue(const String& name, Value value, bool anonymous_setter_enabled, bool named_setter_enabled)
{
setPropertyValue(global_vars, Core::PropertyIndex(name), value, anonymous_setter_enabled, named_setter_enabled);
}
void OS::Core::setGlobalValue(const OS_CHAR * name, Value value, bool anonymous_setter_enabled, bool named_setter_enabled)
{
setGlobalValue(String(allocator, name), value, anonymous_setter_enabled, named_setter_enabled);
}
int OS::Core::getStackOffs(int offs)
{
return offs <= 0 ? stack_values.count + offs + 1 : offs;
}
OS::Core::Value OS::Core::getStackValue(int offs)
{
offs = offs <= 0 ? stack_values.count + offs : offs - 1;
if(offs >= 0 && offs < stack_values.count){
return stack_values.buf[offs];
}
if(offs == OS_REGISTER_GLOBALS - 1){
return global_vars;
}
if(offs == OS_REGISTER_USERPOOL - 1){
return user_pool;
}
// OS_ASSERT(false);
return Value();
}
OS::Core::StackValues::StackValues()
{
buf = NULL;
capacity = 0;
count = 0;
}
OS::Core::StackValues::~StackValues()
{
OS_ASSERT(!buf && !capacity && !count);
}
void OS::Core::reserveStackValues(int new_capacity)
{
if(stack_values.capacity < new_capacity){
stack_values.capacity = (stack_values.capacity*2 + 16) & ~15;
if(stack_values.capacity < new_capacity){
stack_values.capacity = (new_capacity + 16) & ~15;
}
Value * new_buf = (Value*)malloc(sizeof(Value)*stack_values.capacity OS_DBG_FILEPOS);
OS_MEMCPY(new_buf, stack_values.buf, sizeof(Value) * stack_values.count);
free(stack_values.buf);
stack_values.buf = new_buf;
for(int i = 0; i < call_stack_funcs.count; i++){
StackFunction * stack_func = &call_stack_funcs[i];
OS_ASSERT(stack_func->locals_stack_pos >= 0);
OS_ASSERT(stack_func->locals && stack_func->locals->is_stack_locals);
stack_func->locals->locals = stack_values.buf + stack_func->locals_stack_pos;
}
if(stack_func){
stack_func_locals = stack_func->locals->locals;
}
}
}
void OS::Core::removeStackValues(int offs, int count)
{
if(count <= 0){
OS_ASSERT(count == 0);
return;
}
int start = offs <= 0 ? stack_values.count + offs : offs - 1;
if(start < 0 || start >= stack_values.count){
OS_ASSERT(false);
return;
}
int end = start + count;
if(end >= stack_values.count){
OS_ASSERT(end == stack_values.count);
stack_values.count = start;
}else{
count = stack_values.count - end;
if(count == 1){
stack_values.buf[start] = stack_values.buf[end];
}else{
OS_MEMMOVE(stack_values.buf + start, stack_values.buf + end, sizeof(Value) * count);
}
stack_values.count -= end - start;
}
// gcStepIfNeeded();
}
void OS::Core::removeStackValue(int offs)
{
removeStackValues(offs, 1);
}
void OS::Core::removeAllStackValues()
{
stack_values.count = 0;
// gcStepIfNeeded();
}
void OS::Core::pop(int count)
{
if(count >= stack_values.count){
OS_ASSERT(count == stack_values.count);
stack_values.count = 0;
}else{
stack_values.count -= count;
}
// gcStepIfNeeded();
}
void OS::Core::moveStackValues(int offs, int count, int new_offs)
{
if(count <= 0){
OS_ASSERT(count == 0);
return;
}
offs = offs <= 0 ? stack_values.count + offs : offs - 1;
if(offs < 0 || offs >= stack_values.count){
OS_ASSERT(false);
return;
}
int end = offs + count;
if(end > stack_values.count){
OS_ASSERT(false);
return;
}
new_offs = new_offs <= 0 ? stack_values.count + new_offs : new_offs - 1;
if(new_offs < 0 || new_offs >= stack_values.count){
OS_ASSERT(false);
return;
}
int new_end = new_offs + count;
if(new_end > stack_values.count){
OS_ASSERT(false);
return;
}
Value * temp_values = (Value*)alloca(sizeof(Value) * count);
OS_MEMCPY(temp_values, stack_values.buf + offs, sizeof(Value) * count);
if(new_offs > offs){
OS_MEMMOVE(stack_values.buf + offs, stack_values.buf + offs+count, sizeof(Value) * (new_offs - offs));
}else{
OS_MEMMOVE(stack_values.buf + new_offs+count, stack_values.buf + new_offs, sizeof(Value) * (offs - new_offs));
}
OS_MEMCPY(stack_values.buf + new_offs, temp_values, sizeof(Value) * count);
}
void OS::Core::moveStackValue(int offs, int new_offs)
{
offs = offs <= 0 ? stack_values.count + offs : offs - 1;
if(offs < 0 || offs >= stack_values.count){
OS_ASSERT(false);
return;
}
new_offs = new_offs <= 0 ? stack_values.count + new_offs : new_offs - 1;
if(new_offs < 0 || new_offs >= stack_values.count){
OS_ASSERT(false);
return;
}
Value value = stack_values[offs];
if(new_offs > offs){
OS_MEMMOVE(stack_values.buf + offs, stack_values.buf + offs+1, sizeof(Value) * (new_offs - offs));
}else{
OS_MEMMOVE(stack_values.buf + new_offs+1, stack_values.buf + new_offs, sizeof(Value) * (offs - new_offs));
}
stack_values[new_offs] = value;
}
void OS::Core::insertValue(Value val, int offs)
{
offs = offs <= 0 ? stack_values.count + offs : offs - 1;
reserveStackValues(stack_values.count+1);
stack_values.count++;
if(offs < 0 || offs >= stack_values.count){
OS_ASSERT(false);
return;
}
int count = stack_values.count - offs - 1;
if(count > 0){
OS_MEMMOVE(stack_values.buf + offs+1, stack_values.buf + offs, sizeof(Value) * count);
}
stack_values[offs] = val;
}
void OS::pushNull()
{
core->pushNull();
}
void OS::pushNumber(OS_INT32 val)
{
core->pushNumber(val);
}
void OS::pushNumber(OS_INT64 val)
{
core->pushNumber(val);
}
void OS::pushNumber(float val)
{
core->pushNumber(val);
}
void OS::pushNumber(double val)
{
core->pushNumber(val);
}
void OS::pushBool(bool val)
{
core->pushBool(val);
}
void OS::pushString(const OS_CHAR * val)
{
core->pushStringValue(val);
}
void OS::pushString(const OS_CHAR * val, int len)
{
core->pushStringValue(val, len);
}
void OS::pushString(const Core::String& val)
{
core->pushStringValue(val);
}
void OS::pushCFunction(OS_CFunction func, void * user_param)
{
core->pushCFunctionValue(func, user_param);
}
void OS::pushCFunction(OS_CFunction func, int closure_values, void * user_param)
{
core->pushCFunctionValue(func, closure_values, user_param);
}
void * OS::pushUserdata(int crc, int data_size, OS_UserdataDtor dtor, void * user_param)
{
Core::GCUserdataValue * userdata = core->pushUserdataValue(crc, data_size, dtor, user_param);
return userdata ? userdata->ptr : NULL;
}
void * OS::pushUserdata(int data_size, OS_UserdataDtor dtor, void * user_param)
{
return pushUserdata(0, data_size, dtor, user_param);
}
void * OS::pushUserPointer(int crc, void * data, OS_UserdataDtor dtor, void * user_param)
{
Core::GCUserdataValue * userdata = core->pushUserPointerValue(crc, data, dtor, user_param);
return userdata ? userdata->ptr : NULL;
}
void * OS::pushUserPointer(void * data, OS_UserdataDtor dtor, void * user_param)
{
return pushUserPointer(0, data, dtor, user_param);
}
void OS::newObject()
{
core->pushObjectValue();
}
void OS::newArray(int initial_capacity)
{
core->pushArrayValue(initial_capacity);
}
void OS::pushStackValue(int offs)
{
core->pushStackValue(offs);
}
void OS::pushGlobals()
{
pushStackValue(OS_REGISTER_GLOBALS);
}
void OS::pushUserPool()
{
pushStackValue(OS_REGISTER_USERPOOL);
}
void OS::pushValueById(int id)
{
core->pushValue(core->values.get(id));
}
void OS::retainValueById(int id)
{
Core::GCValue * value = core->values.get(id);
if(value){
value->external_ref_count++;
}
}
void OS::releaseValueById(int id)
{
Core::GCValue * value = core->values.get(id);
if(value){
OS_ASSERT(value->external_ref_count > 0);
if(!--value->external_ref_count && value->gc_color == Core::GC_WHITE){
value->gc_color = Core::GC_BLACK;
}
}
}
void OS::clone(int offs)
{
core->pushCloneValue(core->getStackValue(offs));
}
int OS::getStackSize()
{
return core->stack_values.count;
}
int OS::getAbsoluteOffs(int offs)
{
return core->getStackOffs(offs);
}
void OS::remove(int start_offs, int count)
{
core->removeStackValues(start_offs, count);
}
void OS::pop(int count)
{
core->pop(count);
}
void OS::removeAll()
{
core->removeAllStackValues();
}
void OS::move(int start_offs, int count, int new_offs)
{
core->moveStackValues(start_offs, count, new_offs);
}
void OS::move(int offs, int new_offs)
{
core->moveStackValue(offs, new_offs);
}
bool OS::toBool(int offs)
{
return core->valueToBool(core->getStackValue(offs));
}
bool OS::toBool(int offs, bool def)
{
Core::Value value = core->getStackValue(offs);
return value.isNull() ? def : core->valueToBool(value);
}
OS_NUMBER OS::toNumber(int offs, bool valueof_enabled)
{
return core->valueToNumber(core->getStackValue(offs), valueof_enabled);
}
OS_NUMBER OS::toNumber(int offs, OS_NUMBER def, bool valueof_enabled)
{
Core::Value value = core->getStackValue(offs);
return value.isNull() ? def : core->valueToNumber(value, valueof_enabled);
}
float OS::toFloat(int offs, bool valueof_enabled)
{
return (float)toNumber(offs, valueof_enabled);
}
float OS::toFloat(int offs, float def, bool valueof_enabled)
{
return (float)toNumber(offs, (OS_NUMBER)def, valueof_enabled);
}
double OS::toDouble(int offs, bool valueof_enabled)
{
return (double)toNumber(offs, valueof_enabled);
}
double OS::toDouble(int offs, double def, bool valueof_enabled)
{
return (double)toNumber(offs, (OS_NUMBER)def, valueof_enabled);
}
int OS::toInt(int offs, bool valueof_enabled)
{
return (int)toNumber(offs, valueof_enabled);
}
int OS::toInt(int offs, int def, bool valueof_enabled)
{
return (int)toNumber(offs, (OS_NUMBER)def, valueof_enabled);
}
bool OS::isNumber(int offs, OS_NUMBER * out)
{
return core->isValueNumber(core->getStackValue(offs), out);
}
OS::String OS::toString(int offs, bool valueof_enabled)
{
return String(this, core->valueToString(core->getStackValue(offs), valueof_enabled));
}
OS::String OS::toString(int offs, const String& def, bool valueof_enabled)
{
Core::Value value = core->getStackValue(offs);
return value.isNull() ? def : String(this, core->valueToString(value, valueof_enabled));
}
bool OS::isString(int offs, String * out)
{
return core->isValueString(core->getStackValue(offs), out);
}
bool OS::popBool()
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toBool(-1);
}
bool OS::popBool(bool def)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toBool(-1, def);
}
OS_NUMBER OS::popNumber(bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toNumber(-1, valueof_enabled);
}
OS_NUMBER OS::popNumber(OS_NUMBER def, bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toNumber(-1, def, valueof_enabled);
}
float OS::popFloat(bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toFloat(-1, valueof_enabled);
}
float OS::popFloat(float def, bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toFloat(-1, def, valueof_enabled);
}
double OS::popDouble(bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toDouble(-1, valueof_enabled);
}
double OS::popDouble(double def, bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toDouble(-1, def, valueof_enabled);
}
int OS::popInt(bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toInt(-1, valueof_enabled);
}
int OS::popInt(int def, bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toInt(-1, def, valueof_enabled);
}
OS::String OS::popString(bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toString(-1, valueof_enabled);
}
OS::String OS::popString(const String& def, bool valueof_enabled)
{
struct Pop { OS * os; ~Pop(){ os->pop(); } } pop = {this};
return toString(-1, def, valueof_enabled);
}
OS_EValueType OS::getType(int offs)
{
return core->getStackValue(offs).type;
}
OS_EValueType OS::getTypeById(int id)
{
Core::GCValue * val = core->values.get(id);
return val ? val->type : OS_VALUE_TYPE_NULL;
}
bool OS::isType(OS_EValueType type, int offs)
{
return core->getStackValue(offs).type == type;
}
bool OS::isNull(int offs)
{
return isType(OS_VALUE_TYPE_NULL, offs);
}
bool OS::isObject(int offs)
{
switch(core->getStackValue(offs).type){
case OS_VALUE_TYPE_OBJECT:
// case OS_VALUE_TYPE_ARRAY:
return true;
}
return false;
}
bool OS::isUserdata(int crc, int offs)
{
Core::Value val = core->getStackValue(offs);
switch(val.type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
return val.v.userdata->crc == crc;
}
return false;
}
void * OS::toUserdata(int crc, int offs)
{
Core::Value val = core->getStackValue(offs);
switch(val.type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
if(val.v.userdata->crc == crc){
return val.v.userdata->ptr;
}
}
return NULL;
}
void OS::clearUserdata(int crc, int offs)
{
Core::Value val = core->getStackValue(offs);
switch(val.type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
if(val.v.userdata->crc == crc){ // && val.v.userdata->ptr){
core->clearValue(val.v.value);
// val.v.userdata->ptr = NULL;
}
}
}
bool OS::isArray(int offs)
{
return isType(OS_VALUE_TYPE_ARRAY, offs);
}
bool OS::isFunction(int offs)
{
return core->getStackValue(offs).isFunction();
}
bool OS::Core::isValuePrototypeOf(GCValue * val, GCValue * prototype_val)
{
while(val != prototype_val){
val = val->prototype;
if(!val){
return false;
}
}
return true;
}
bool OS::Core::isValueInstanceOf(GCValue * val, GCValue * prototype_val)
{
return val->prototype ? isValuePrototypeOf(val->prototype, prototype_val) : false;
}
bool OS::Core::isValuePrototypeOf(Value val, Value prototype_val)
{
GCValue * object = val.getGCValue();
GCValue * proto = prototype_val.getGCValue();
return object && proto && isValuePrototypeOf(object, proto);
}
bool OS::Core::isValueInstanceOf(Value val, Value prototype_val)
{
GCValue * object = val.getGCValue();
GCValue * proto = prototype_val.getGCValue();
return object && proto && isValueInstanceOf(object, proto);
}
bool OS::isPrototypeOf(int value_offs, int prototype_offs)
{
return core->isValuePrototypeOf(core->getStackValue(value_offs), core->getStackValue(prototype_offs));
}
bool OS::is(int value_offs, int prototype_offs)
{
return core->isValueInstanceOf(core->getStackValue(value_offs), core->getStackValue(prototype_offs));
}
void OS::setProperty(bool anonymous_setter_enabled, bool named_setter_enabled)
{
if(core->stack_values.count >= 3){
Core::Value object = core->stack_values[core->stack_values.count - 3];
Core::Value index = core->stack_values[core->stack_values.count - 2];
Core::Value value = core->stack_values[core->stack_values.count - 1];
core->setPropertyValue(object, Core::PropertyIndex(index), value, anonymous_setter_enabled, named_setter_enabled);
pop(3);
}else{
// error
pop(3);
}
}
void OS::setProperty(const OS_CHAR * name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
setProperty(Core::String(this, name), anonymous_setter_enabled, named_setter_enabled);
}
void OS::setProperty(const Core::String& name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
if(core->stack_values.count >= 2){
Core::Value object = core->stack_values[core->stack_values.count - 2];
Core::Value value = core->stack_values[core->stack_values.count - 1];
core->setPropertyValue(object, Core::PropertyIndex(name), value, anonymous_setter_enabled, named_setter_enabled);
pop(2);
}else{
// error
pop(2);
}
}
void OS::setProperty(int offs, const OS_CHAR * name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
setProperty(offs, Core::String(this, name), anonymous_setter_enabled, named_setter_enabled);
}
void OS::setProperty(int offs, const Core::String& name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
if(core->stack_values.count >= 1){
Core::Value object = core->getStackValue(offs);
Core::Value value = core->stack_values[core->stack_values.count - 1];
core->setPropertyValue(object, Core::PropertyIndex(name), value, anonymous_setter_enabled, named_setter_enabled);
pop();
}else{
// error
pop();
}
}
void OS::addProperty()
{
Core::Value value = core->getStackValue(-2);
switch(value.type){
case OS_VALUE_TYPE_ARRAY:
core->insertValue(value.v.arr->values.count, -1);
break;
case OS_VALUE_TYPE_OBJECT:
core->insertValue(value.v.object->table ? value.v.object->table->next_index : 0, -1);
break;
}
setProperty(false, false);
}
void OS::deleteProperty(bool anonymous_del_enabled, bool named_del_enabled)
{
core->deleteValueProperty(core->getStackValue(-2), core->getStackValue(-1), anonymous_del_enabled, named_del_enabled, false);
pop(2);
}
void OS::deleteProperty(const OS_CHAR * name, bool anonymous_del_enabled, bool named_del_enabled)
{
deleteProperty(Core::String(this, name), anonymous_del_enabled, named_del_enabled);
}
void OS::deleteProperty(const Core::String& name, bool anonymous_del_enabled, bool named_del_enabled)
{
pushString(name);
deleteProperty(anonymous_del_enabled, named_del_enabled);
}
void OS::getPrototype()
{
if(core->stack_values.count >= 1){
core->pushPrototype(core->stack_values.lastElement());
}else{
pushNull();
}
}
void OS::setPrototype()
{
setPrototype(0);
}
void OS::setPrototype(int userdata_crc)
{
if(core->stack_values.count >= 2){
Core::Value value = core->stack_values[core->stack_values.count - 2];
Core::Value proto = core->stack_values[core->stack_values.count - 1];
core->setPrototype(value, proto, userdata_crc);
}
pop(2);
}
int OS::getValueId(int offs)
{
Core::Value val = core->getStackValue(offs);
switch(val.type){
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return val.v.value->value_id;
case OS_VALUE_TYPE_WEAKREF:
return val.v.value_id;
}
return 0;
}
bool OS::Core::getPropertyValue(Value& result, Table * table, const PropertyIndex& index)
{
#if defined OS_DEBUG && defined OS_WARN_NULL_INDEX
if(table != check_recursion->table && index.index.type == OS_VALUE_TYPE_NULL){
error(OS_E_WARNING, OS_TEXT("object get null index"));
}
#endif
if(table){
Property * prop = table->get(index);
if(prop){
result = prop->value;
return true;
}
}
return false;
}
bool OS::Core::getPropertyValue(Value& result, GCValue * table_value, const PropertyIndex& index, bool prototype_enabled)
{
#if defined OS_DEBUG && defined OS_WARN_NULL_INDEX
if(table_value != check_recursion && index.index.type == OS_VALUE_TYPE_NULL){
error(OS_E_WARNING, OS_TEXT("object get null index"));
}
#endif
if(table_value->type == OS_VALUE_TYPE_ARRAY && index.index.type == OS_VALUE_TYPE_NUMBER){
OS_ASSERT(dynamic_cast<GCArrayValue*>(table_value));
int i = (int)index.index.v.number;
if((i >= 0 || (i += ((GCArrayValue*)table_value)->values.count) >= 0) && i < ((GCArrayValue*)table_value)->values.count){
result = ((GCArrayValue*)table_value)->values[i];
}else{
result = Value();
}
return true;
}
Property * prop = NULL;
Table * table = table_value->table;
if(table && (prop = table->get(index))){
result = prop->value;
return true;
}
if(prototype_enabled){
GCValue * cur_value = table_value;
while(cur_value->prototype){
cur_value = cur_value->prototype;
Table * cur_table = cur_value->table;
if(cur_table && (prop = cur_table->get(index))){
result = prop->value;
return true;
}
}
}
if(index.index.type == OS_VALUE_TYPE_STRING && strings->syntax_prototype == index.index.v.string){
result = table_value->prototype;
return true;
}
if(table_value->type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<GCArrayValue*>(table_value));
OS_NUMBER number;
if(isValueNumber(index.index, &number)){
int i = (int)number;
if((i >= 0 || (i += ((GCArrayValue*)table_value)->values.count) >= 0) && i < ((GCArrayValue*)table_value)->values.count){
result = ((GCArrayValue*)table_value)->values[i];
}else{
result = Value();
}
return true;
}
}
return false;
}
bool OS::Core::getPropertyValue(Value& result, Value table_value, const PropertyIndex& index, bool prototype_enabled)
{
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
return false;
case OS_VALUE_TYPE_BOOL:
return prototype_enabled && getPropertyValue(result, prototypes[PROTOTYPE_BOOL], index, prototype_enabled);
case OS_VALUE_TYPE_NUMBER:
return prototype_enabled && getPropertyValue(result, prototypes[PROTOTYPE_NUMBER], index, prototype_enabled);
case OS_VALUE_TYPE_STRING:
// return prototype_enabled && getPropertyValue(result, prototypes[PROTOTYPE_STRING], index, prototype_enabled);
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return getPropertyValue(result, table_value.v.value, index, prototype_enabled);
}
return false;
}
bool OS::Core::hasProperty(GCValue * table_value, const PropertyIndex& index, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
Value value;
if(getPropertyValue(value, table_value, index, prototype_enabled)){
return value.type != OS_VALUE_TYPE_NULL;
}
if(!anonymous_getter_enabled && !named_getter_enabled){
return false;
}
if(hasSpecialPrefix(index.index)){
return false;
}
if(index.index.type == OS_VALUE_TYPE_STRING && named_getter_enabled){
const void * buf1 = strings->__getAt.toChar();
int size1 = strings->__getAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * getter_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(value, table_value, PropertyIndex(getter_name, PropertyIndex::KeepStringIndex()), prototype_enabled)){
return true;
}
}
if(anonymous_getter_enabled){
// TODO: add __isset method ???
}
return false;
}
void OS::Core::pushPropertyValue(GCValue * table_value, const PropertyIndex& index, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled, bool auto_create)
{
GCValue * self = table_value;
for(;;){
Value value;
if(getPropertyValue(value, table_value, index, prototype_enabled)){
return pushValue(value);
}
if((anonymous_getter_enabled || named_getter_enabled) && !hasSpecialPrefix(index.index)){
if(index.index.type == OS_VALUE_TYPE_STRING && named_getter_enabled){
const void * buf1 = strings->__getAt.toChar();
int size1 = strings->__getAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * getter_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(value, table_value, PropertyIndex(getter_name, PropertyIndex::KeepStringIndex()), prototype_enabled)){
pushValue(value);
pushValue(self);
call(0, 1);
return;
}
}
if(anonymous_getter_enabled && getPropertyValue(value, table_value, PropertyIndex(strings->__get, PropertyIndex::KeepStringIndex()), prototype_enabled)){
// auto_create = false;
if(value.type == OS_VALUE_TYPE_OBJECT){
table_value = value.v.value;
continue;
}
pushValue(value);
pushValue(self);
pushValue(index.index);
if(!auto_create){
call(1, 1);
}else{
pushBool(true);
call(2, 1);
}
if(auto_create && stack_values.lastElement().type == OS_VALUE_TYPE_NULL){
pop();
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
}
return;
}
}
if(auto_create){
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
return;
}
break;
}
return pushNull();
}
void OS::Core::pushPropertyValueForPrimitive(Value self, const PropertyIndex& index, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled, bool auto_create)
{
GCValue * proto;
switch(self.type){
case OS_VALUE_TYPE_NUMBER:
proto = prototypes[PROTOTYPE_NUMBER];
break;
case OS_VALUE_TYPE_BOOL:
proto = prototypes[PROTOTYPE_BOOL];
break;
default:
pushNull();
return;
}
// GCValue * self = table_value;
for(;;){
OS_ASSERT(proto);
Value value;
if(prototype_enabled && getPropertyValue(value, proto, index, prototype_enabled)){
return pushValue(value);
}
if((anonymous_getter_enabled || named_getter_enabled) && !hasSpecialPrefix(index.index)){
if(index.index.type == OS_VALUE_TYPE_STRING && named_getter_enabled){
const void * buf1 = strings->__getAt.toChar();
int size1 = strings->__getAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * getter_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(value, proto, PropertyIndex(getter_name, PropertyIndex::KeepStringIndex()), prototype_enabled)){
pushValue(value);
pushValue(self);
call(0, 1);
return;
}
}
if(anonymous_getter_enabled && getPropertyValue(value, proto, PropertyIndex(strings->__get, PropertyIndex::KeepStringIndex()), prototype_enabled)){
// auto_create = false;
if(value.type == OS_VALUE_TYPE_OBJECT){
proto = value.v.value;
continue;
}
pushValue(value);
pushValue(self);
pushValue(index.index);
if(!auto_create){
call(1, 1);
}else{
pushBool(true);
call(2, 1);
}
if(auto_create && stack_values.lastElement().type == OS_VALUE_TYPE_NULL){
pop();
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
}
return;
}
}
if(auto_create){
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
return;
}
break;
}
return pushNull();
}
void OS::Core::pushPropertyValue(Value table_value, const PropertyIndex& index, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled, bool auto_create)
{
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
break;
case OS_VALUE_TYPE_BOOL:
/* if(prototype_enabled){
return pushPropertyValue(prototypes[PROTOTYPE_BOOL], index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
}
break; */
case OS_VALUE_TYPE_NUMBER:
/* if(prototype_enabled){
return pushPropertyValue(prototypes[PROTOTYPE_NUMBER], index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
}
break; */
return pushPropertyValueForPrimitive(table_value, index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
case OS_VALUE_TYPE_STRING:
/* if(prototype_enabled){
return pushPropertyValue(prototypes[PROTOTYPE_STRING], index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
}
break; */
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
return pushPropertyValue(table_value.v.value, index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
}
pushNull();
}
void OS::getProperty(bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
if(core->stack_values.count >= 2){
Core::Value object = core->stack_values[core->stack_values.count - 2];
Core::Value index = core->stack_values[core->stack_values.count - 1];
// core->stack_values.count -= 2;
core->pushPropertyValue(object, Core::PropertyIndex(index), anonymous_getter_enabled, named_getter_enabled, prototype_enabled, false);
core->removeStackValues(-3, 2);
}else{
// error
pop(2);
pushNull();
}
}
void OS::getProperty(const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
getProperty(Core::String(this, name), anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::getProperty(const Core::String& name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
pushString(name);
getProperty(anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::getProperty(int offs, const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
getProperty(offs, Core::String(this, name), anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::getProperty(int offs, const Core::String& name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
pushStackValue(offs);
getProperty(name, anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::Core::releaseUpvalues(Upvalues * upvalues)
{
if(--upvalues->ref_count > 0){
return;
}
deleteUpvalues(upvalues);
}
void OS::Core::deleteUpvalues(Upvalues * upvalues)
{
upvalues->prog->release();
if(upvalues->num_parents > 0){
releaseUpvalues(upvalues->getParent(0));
}
if(!upvalues->is_stack_locals){
free(upvalues->locals);
}
free(upvalues);
}
void OS::Core::clearStackFunction(StackFunction * stack_func)
{
// OS_ASSERT(stack_func->func && stack_func->func->type == OS_VALUE_TYPE_FUNCTION);
// OS_ASSERT(stack_func->func->value.func->func_decl);
// OS_ASSERT(stack_func->self);
// OS_ASSERT(stack_func->func->value.func->parent_inctance != stack_func);
if(--stack_func->locals->ref_count > 0){
int count = stack_func->locals->num_locals; // >opcode_stack_pos - stack_func->locals_stack_pos;
if(count > 0){
Value * locals = (Value*)malloc(sizeof(Value) * count OS_DBG_FILEPOS);
OS_MEMCPY(locals, stack_func->locals->locals, sizeof(Value) * count);
stack_func->locals->locals = locals;
}else{
stack_func->locals->locals = NULL;
}
stack_func->locals->is_stack_locals = false;
}else{
deleteUpvalues(stack_func->locals);
}
stack_func->func = NULL;
stack_func->self = NULL;
stack_func->self_for_proto = NULL;
stack_func->locals = NULL;
stack_func->arguments = NULL;
stack_func->rest_arguments = NULL;
stack_func->~StackFunction();
// free(stack_func);
}
void OS::Core::enterFunction(GCFunctionValue * func_value, Value self, GCValue * self_for_proto, int params, int extra_remove_from_stack, int need_ret_values)
{
OS_ASSERT(call_stack_funcs.count < OS_CALL_STACK_MAX_SIZE);
OS_ASSERT(func_value->type == OS_VALUE_TYPE_FUNCTION);
OS_ASSERT(stack_values.count >= params + extra_remove_from_stack);
// OS_ASSERT(self);
FunctionDecl * func_decl = func_value->func_decl;
int num_extra_params = params > func_decl->num_params ? params - func_decl->num_params : 0;
// int locals_mem_size = sizeof(Value) * (func_decl->num_locals + num_extra_params);
// int parents_mem_size = sizeof(FunctionRunningInstance*) * func_decl->max_up_count;
// stack has to be reserved here!!! don't move it
reserveStackValues(stack_values.count - params + func_decl->num_locals + num_extra_params);
// allocator->vectorReserveCapacity(call_stack_funcs, call_stack_funcs.count+1 OS_DBG_FILEPOS);
if(call_stack_funcs.capacity < call_stack_funcs.count+1){
call_stack_funcs.capacity = call_stack_funcs.capacity > 0 ? call_stack_funcs.capacity*2 : 8;
OS_ASSERT(call_stack_funcs.capacity >= call_stack_funcs.count+1);
StackFunction * new_buf = (StackFunction*)malloc(sizeof(StackFunction)*call_stack_funcs.capacity OS_DBG_FILEPOS);
OS_MEMCPY(new_buf, call_stack_funcs.buf, sizeof(StackFunction) * call_stack_funcs.count);
free(call_stack_funcs.buf);
call_stack_funcs.buf = new_buf;
}
// StackFunction * stack_func = new (malloc(sizeof(StackFunction) OS_DBG_FILEPOS)) StackFunction();
// StackFunction * stack_func = new (call_stack_funcs.buf + call_stack_funcs.count++) StackFunction();
StackFunction * stack_func = (StackFunction*)(call_stack_funcs.buf + call_stack_funcs.count++);
stack_func->func = func_value;
stack_func->self = self;
stack_func->self_for_proto = self_for_proto ? self_for_proto : self.getGCValue();
if(!stack_func->self_for_proto){
switch(self.type){
case OS_VALUE_TYPE_BOOL:
stack_func->self_for_proto = prototypes[PROTOTYPE_BOOL];
break;
case OS_VALUE_TYPE_NUMBER:
stack_func->self_for_proto = prototypes[PROTOTYPE_NUMBER];
break;
}
}
stack_func->caller_stack_pos = stack_values.count - params - extra_remove_from_stack;
stack_func->locals_stack_pos = stack_func->caller_stack_pos + extra_remove_from_stack;
stack_func->stack_pos = stack_func->locals_stack_pos + func_decl->num_locals + num_extra_params;
// reserveStackValues(stack_func->bottom_stack_pos);
OS_ASSERT(stack_values.capacity >= stack_func->stack_pos);
stack_values.count = stack_func->stack_pos;
stack_func->num_params = params;
stack_func->num_extra_params = num_extra_params;
Upvalues * func_locals = (Upvalues*)(malloc(sizeof(Upvalues) + sizeof(Upvalues*) * func_decl->func_depth OS_DBG_FILEPOS));
func_locals->prog = func_value->prog->retain();
func_locals->func_decl = func_decl;
func_locals->locals = stack_values.buf + stack_func->locals_stack_pos;
func_locals->num_locals = func_decl->num_locals;
func_locals->is_stack_locals = true;
func_locals->num_parents = func_decl->func_depth;
func_locals->ref_count = 1;
func_locals->gc_time = -1;
if(func_decl->func_depth > 0){
OS_ASSERT(func_value->upvalues && func_value->upvalues->num_parents == func_decl->func_depth-1);
Upvalues ** parents = func_locals->getParents();
parents[0] = func_value->upvalues->retain();
if(func_decl->func_depth > 1){
OS_MEMCPY(parents+1, func_value->upvalues->getParents(), sizeof(Upvalues*) * (func_decl->func_depth-1));
}
}
stack_func->locals = func_locals;
Value * extra_params = func_locals->locals + func_decl->num_locals;
if(num_extra_params > 0 && func_decl->num_locals != params - num_extra_params){
OS_MEMCPY(extra_params, func_locals->locals + (params - num_extra_params), sizeof(Value) * num_extra_params);
}
int func_params = func_decl->num_params < params ? func_decl->num_params : params;
OS_ASSERT(func_params <= func_decl->num_locals);
if(func_decl->num_locals > func_params){
OS_MEMSET(func_locals->locals + func_params, 0, sizeof(Value) * (func_decl->num_locals - func_params));
}
stack_func->need_ret_values = need_ret_values;
// stack_func->opcode_offs = 0; // func_decl->opcodes_pos;
new (&stack_func->opcodes) MemStreamReader(NULL, func_value->prog->opcodes->buffer + func_decl->opcodes_pos, func_decl->opcodes_size);
func_locals->locals[func_decl->num_params + ENV_VAR_INDEX] = func_value->env;
#ifdef OS_GLOBAL_VAR_ENABLED
func_locals->locals[func_decl->num_params + GLOBALS_VAR_INDEX] = global_vars;
#endif
reloadStackFunctionCache();
// gcMarkStackFunction(stack_func);
}
int OS::Core::opBreakFunction()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= stack_func->stack_pos);
int cur_ret_values = 0;
int ret_values = stack_func->need_ret_values;
stack_values.count = stack_func->stack_pos;
ret_values = syncRetValues(ret_values, cur_ret_values);
OS_ASSERT(stack_values.count == stack_func->stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
clearStackFunction(stack_func);
removeStackValues(stack_func->caller_stack_pos+1, stack_values.count - ret_values - stack_func->caller_stack_pos);
reloadStackFunctionCache();
return ret_values;
}
void OS::Core::opDebugger()
{
StackFunction * stack_func = this->stack_func;
int line = stack_func->opcodes.readUVariable();
int pos = stack_func->opcodes.readUVariable();
int saved_lines = stack_func->opcodes.readUVariable();
const OS_CHAR * lines[OS_DEBUGGER_SAVE_NUM_LINES];
OS_MEMSET(lines, 0, sizeof(lines));
GCStringValue ** prog_strings = stack_func_prog_strings; // stack_func->func->prog->const_strings;
int prog_num_strings = stack_func->func->prog->num_strings;
for(int i = 0; i < saved_lines; i++){
int offs = stack_func->opcodes.readUVariable();
OS_ASSERT(offs >= 0 && offs < prog_num_strings);
OS_ASSERT(prog_strings[offs]->type == OS_VALUE_TYPE_STRING);
if(i < OS_DEBUGGER_SAVE_NUM_LINES){
lines[i] = prog_strings[offs]->toChar();
}
}
DEBUG_BREAK;
}
/*
void OS::Core::opPushNumber()
{
// StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_numbers);
pushNumber(stack_func_prog_numbers[i]);
}
void OS::Core::opPushString()
{
StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
OS_ASSERT(stack_func_prog_strings[i]->type == OS_VALUE_TYPE_STRING);
pushValue(stack_func_prog_strings[i]);
}
*/
void OS::Core::opPushFunction()
{
StackFunction * stack_func = this->stack_func;
int prog_func_index = stack_func->opcodes.readUVariable();
Program * prog = stack_func->func->prog;
OS_ASSERT(prog_func_index > 0 && prog_func_index < prog->num_functions);
FunctionDecl * func_decl = prog->functions + prog_func_index;
// int env_index = stack_func->func->func_decl->num_params + ENV_VAR_INDEX;
pushValue(newFunctionValue(stack_func, prog, func_decl, stack_func_locals[stack_func_env_index]));
stack_func->opcodes.movePos(func_decl->opcodes_size);
}
void OS::Core::opPushArray()
{
pushArrayValue(stack_func->opcodes.readByte());
}
void OS::Core::opPushObject()
{
pushObjectValue();
}
void OS::Core::opObjectSetByAutoIndex()
{
OS_ASSERT(stack_values.count >= 2);
Value object = stack_values[stack_values.count-2];
OS_INT num_index = 0;
switch(object.type){
case OS_VALUE_TYPE_ARRAY:
OS_ASSERT(dynamic_cast<GCArrayValue*>(object.v.arr));
num_index = object.v.arr->values.count;
break;
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
num_index = object.v.object->table ? object.v.object->table->next_index : 0;
break;
}
setPropertyValue(object, PropertyIndex(num_index), stack_values.lastElement(), false, false);
pop(); // keep only object in stack
}
void OS::Core::opObjectSetByExp()
{
OS_ASSERT(stack_values.count >= 3);
setPropertyValue(stack_values[stack_values.count - 3],
Core::PropertyIndex(stack_values[stack_values.count - 2]),
stack_values[stack_values.count - 1], false, false);
pop(2); // keep only object in stack
}
void OS::Core::opObjectSetByIndex()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 2);
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_numbers);
setPropertyValue(stack_values[stack_values.count-2],
PropertyIndex(stack_func->func->prog->const_numbers[i]),
stack_values.lastElement(), false, false);
pop(); // keep only object in stack
}
void OS::Core::opObjectSetByName()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 2);
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
GCStringValue * name = stack_func_prog_strings[i];
OS_ASSERT(name->type == OS_VALUE_TYPE_STRING);
setPropertyValue(stack_values[stack_values.count-2],
PropertyIndex(name, PropertyIndex::KeepStringIndex()),
stack_values.lastElement(), false, false);
pop(); // keep only object in stack
}
void OS::Core::opPushEnvVar(bool auto_create)
{
StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
GCStringValue * name = stack_func_prog_strings[i];
OS_ASSERT(name->type == OS_VALUE_TYPE_STRING);
// int env_index = stack_func->func->func_decl->num_params + ENV_VAR_INDEX;
pushPropertyValue(stack_func_locals[stack_func_env_index],
PropertyIndex(name, PropertyIndex::KeepStringIndex()),
true, true, true, auto_create);
}
void OS::Core::opSetEnvVar()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
GCStringValue * name = stack_func_prog_strings[i];
OS_ASSERT(name->type == OS_VALUE_TYPE_STRING);
// int env_index = stack_func->func->func_decl->num_params + ENV_VAR_INDEX;
setPropertyValue(stack_func_locals[stack_func_env_index],
PropertyIndex(name, PropertyIndex::KeepStringIndex()),
stack_values.lastElement(), true, true);
pop();
}
void OS::Core::opPushThis()
{
StackFunction * stack_func = this->stack_func;
pushValue(stack_func->self);
}
void OS::Core::opPushArguments()
{
StackFunction * stack_func = this->stack_func;
pushArguments(stack_func);
}
void OS::Core::opPushRestArguments()
{
StackFunction * stack_func = this->stack_func;
pushRestArguments(stack_func);
}
void OS::Core::opPushLocalVar()
{
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i < num_stack_func_locals);
pushValue(stack_func_locals[i]);
}
void OS::Core::opPushLocalVarAutoCreate()
{
// StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i < num_stack_func_locals);
if(stack_func_locals[i].type == OS_VALUE_TYPE_NULL){
stack_func_locals[i] = newObjectValue();
}
pushValue(stack_func_locals[i]);
}
void OS::Core::opSetLocalVar()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
int i = stack_func->opcodes.readUVariable();
// Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(i < num_stack_func_locals);
StackValues * stack_values = &this->stack_values;
switch((stack_func_locals[i] = stack_values->buf[--stack_values->count]).type){
case OS_VALUE_TYPE_FUNCTION:
OS_ASSERT(dynamic_cast<GCFunctionValue*>(stack_func_locals[i].v.func));
if(!stack_func_locals[i].v.func->name){
stack_func_locals[i].v.func->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
case OS_VALUE_TYPE_CFUNCTION:
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(stack_func_locals[i].v.cfunc));
if(!stack_func_locals[i].v.cfunc->name){
stack_func_locals[i].v.cfunc->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
}
// already removed
// pop();
}
void OS::Core::opPushUpvalue()
{
StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int up_count = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(up_count <= stack_func->func->func_decl->max_up_count);
Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(up_count <= func_upvalues->num_parents);
Upvalues * scope = func_upvalues->getParent(up_count-1);
OS_ASSERT(i < scope->num_locals);
pushValue(scope->locals[i]);
}
void OS::Core::opPushUpvalueAutoCreate()
{
StackFunction * stack_func = this->stack_func;
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int up_count = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(up_count <= stack_func->func->func_decl->max_up_count);
Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(up_count <= func_upvalues->num_parents);
Upvalues * scope = func_upvalues->getParent(up_count-1);
OS_ASSERT(i < scope->num_locals);
if(scope->locals[i].type == OS_VALUE_TYPE_NULL){
scope->locals[i] = newObjectValue();
}
pushValue(scope->locals[i]);
}
void OS::Core::opSetUpvalue()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int up_count = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(up_count <= stack_func->func->func_decl->max_up_count);
Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(up_count <= func_upvalues->num_parents);
Upvalues * scope = func_upvalues->getParent(up_count-1);
OS_ASSERT(i < scope->num_locals);
switch((scope->locals[i] = stack_values.lastElement()).type){
case OS_VALUE_TYPE_FUNCTION:
OS_ASSERT(dynamic_cast<GCFunctionValue*>(scope->locals[i].v.func));
if(!scope->locals[i].v.func->name){
scope->locals[i].v.func->name = scope->func_decl->locals[i].name.string;
}
break;
case OS_VALUE_TYPE_CFUNCTION:
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(scope->locals[i].v.cfunc));
if(!scope->locals[i].v.cfunc->name){
scope->locals[i].v.cfunc->name = scope->func_decl->locals[i].name.string;
}
break;
}
// pop();
--stack_values.count;
}
void OS::Core::opIfJump1(bool boolean)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) == boolean){
// int offs = stack_func->opcodes.readInt16();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int offs = (OS_INT8)stack_func->opcodes.cur[0];
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
}
// pop();
--stack_values.count;
}
void OS::Core::opIfJump2(bool boolean)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) == boolean){
// int offs = stack_func->opcodes.readInt16();
OS_ASSERT(stack_func->opcodes.getPos()+2 <= stack_func->opcodes.getSize());
int offs = (OS_INT16)(stack_func->opcodes.cur[0] | (stack_func->opcodes.cur[1] << 8));
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
}
// pop();
--stack_values.count;
}
void OS::Core::opIfJump4(bool boolean)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) == boolean){
int offs = stack_func->opcodes.readInt32();
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
}
// pop();
--stack_values.count;
}
void OS::Core::opJump4()
{
StackFunction * stack_func = this->stack_func;
int offs = stack_func->opcodes.readInt32();
stack_func->opcodes.movePos(offs);
}
void OS::Core::opCall()
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int params = buf[0];
int ret_values = buf[1];
#else
int params = stack_func->opcodes.readByte();
int ret_values = stack_func->opcodes.readByte();
#endif
OS_ASSERT(stack_values.count >= 2 + params);
// insertValue(Value(), -params);
call(params, ret_values, NULL, true);
}
void OS::Core::opSuperCall(int& break_with_ret_values)
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int params = buf[0];
int ret_values = buf[1];
#else
int params = stack_func->opcodes.readByte();
int ret_values = stack_func->opcodes.readByte();
#endif
OS_ASSERT(stack_values.count >= 2+params);
OS_ASSERT(stack_values.buf[stack_values.count-2-params].type == OS_VALUE_TYPE_NULL);
OS_ASSERT(stack_values.buf[stack_values.count-1-params].type == OS_VALUE_TYPE_NULL);
GCFunctionValue * func_value = stack_func->func;
if(stack_func->self_for_proto && func_value->name){
GCValue * proto = stack_func->self_for_proto->prototype;
if(stack_func->self_for_proto->is_object_instance){
proto = proto ? proto->prototype : NULL;
}
if(proto){
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, proto,
PropertyIndex(func_value->name, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
bool is_constructor = func_value->name == strings->__construct.string;
stack_values.buf[stack_values.count-2-params] = func;
stack_values.buf[stack_values.count-1-params] = stack_func->self;
int func_ret_values = call(params, is_constructor && !ret_values ? 1 : ret_values, proto);
if(is_constructor){
GCValue * new_self = stack_values.lastElement().getGCValue();
if(!new_self){
int cur_ret_values = 0;
int ret_values = stack_func->need_ret_values;
ret_values = syncRetValues(ret_values, cur_ret_values);
// OS_ASSERT(stack_values.count == stack_func->bottom_stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
int caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - ret_values - caller_stack_pos);
reloadStackFunctionCache();
break_with_ret_values = ret_values;
return;
}
if(new_self != stack_func->self.getGCValue()){
stack_func->self = new_self;
stack_func->self_for_proto = new_self;
}
}
syncRetValues(ret_values, func_ret_values);
return;
}
}
}
pop(2+params);
syncRetValues(ret_values, 0);
}
void OS::Core::opTailCall(int& out_ret_values)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int params = *stack_func->opcodes.cur++; // readByte();
int ret_values = stack_func->need_ret_values;
OS_ASSERT(stack_values.count >= 1 + params);
Value func_value = stack_values[stack_values.count-1-params];
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
switch(func_value.type){
case OS_VALUE_TYPE_CFUNCTION:
case OS_VALUE_TYPE_OBJECT:
// pushNull();
// moveStackValue(-1, -params);
insertValue(Value(), -params);
call(params, ret_values);
break;
case OS_VALUE_TYPE_FUNCTION:
{
call_stack_funcs.count--;
int caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - 1-params - caller_stack_pos);
enterFunction(func_value.v.func, NULL, NULL, params, 1, ret_values);
return;
}
default:
// TODO: warn or error here???
pop(1+params);
ret_values = syncRetValues(ret_values, 0);
}
OS_ASSERT(stack_values.count == stack_func->stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
int caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - ret_values - caller_stack_pos);
reloadStackFunctionCache();
out_ret_values = ret_values;
}
void OS::Core::opCallMethod()
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int params = buf[0];
int ret_values = buf[1];
#else
int params = stack_func->opcodes.readByte();
int ret_values = stack_func->opcodes.readByte();
#endif
OS_ASSERT(stack_values.count >= 2 + params);
Value table_value = stack_values[stack_values.count-2-params];
pushPropertyValue(table_value, PropertyIndex(stack_values[stack_values.count-1-params]), true, true, true, false);
#if 1 // speed optimization
stack_values[stack_values.count-2-params-1] = stack_values.lastElement();
stack_values[stack_values.count-2-params-0] = table_value;
stack_values.count--;
call(params, ret_values, NULL, true);
#else
pushValue(table_value);
moveStackValues(-2, 2, -2-params);
call(params, ret_values);
removeStackValues(-2-ret_values, 2);
return false;
#endif
}
void OS::Core::opTailCallMethod(int& out_ret_values)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int params = *stack_func->opcodes.cur++; // readByte();
int ret_values = stack_func->need_ret_values;
OS_ASSERT(stack_values.count >= 2 + params);
Value table_value = stack_values[stack_values.count-2-params];
pushPropertyValue(table_value, Core::PropertyIndex(stack_values[stack_values.count-1-params]), true, true, true, false);
Value func_value = stack_values.lastElement();
GCValue * self = stack_func->self.getGCValue();
GCValue * self_for_proto = stack_func->self_for_proto;
GCValue * call_self = table_value.getGCValue();
if(call_self && (!self || self->prototype != call_self)){
self = call_self;
self_for_proto = call_self;
}
pushValue(self);
moveStackValues(-2, 2, -2-params);
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
int caller_stack_pos;
switch(func_value.type){
case OS_VALUE_TYPE_CFUNCTION:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
call(params, ret_values);
removeStackValues(-2-ret_values, 2);
break;
case OS_VALUE_TYPE_FUNCTION:
call_stack_funcs.count--;
caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - 4-params - caller_stack_pos);
enterFunction(func_value.v.func, self, self_for_proto, params, 4, ret_values);
return;
default:
// TODO: warn or error here???
pop(4+params);
ret_values = syncRetValues(ret_values, 0);
}
OS_ASSERT(stack_values.count == stack_func->stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - ret_values - caller_stack_pos);
reloadStackFunctionCache();
out_ret_values = ret_values;
}
int OS::Core::opReturn()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int cur_ret_values = *stack_func->opcodes.cur++; // readByte();
int ret_values = stack_func->need_ret_values;
ret_values = syncRetValues(ret_values, cur_ret_values);
OS_ASSERT(stack_values.count == stack_func->stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
int caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - ret_values - caller_stack_pos);
reloadStackFunctionCache();
return ret_values;
}
int OS::Core::opReturnAuto()
{
StackFunction * stack_func = this->stack_func;
int cur_ret_values = 0;
int ret_values = stack_func->need_ret_values;
GCFunctionValue * func_value = stack_func->func;
if(ret_values > 0 && func_value->name && func_value->name == strings->__construct.string){
pushValue(stack_func->self);
cur_ret_values = 1;
}
ret_values = syncRetValues(ret_values, cur_ret_values);
// OS_ASSERT(stack_values.count == stack_func->stack_pos + ret_values);
// stack_func->opcodes_pos = opcodes.getPos();
OS_ASSERT(call_stack_funcs.count > 0 && &call_stack_funcs[call_stack_funcs.count-1] == stack_func);
call_stack_funcs.count--;
int caller_stack_pos = stack_func->caller_stack_pos;
clearStackFunction(stack_func);
removeStackValues(caller_stack_pos+1, stack_values.count - ret_values - caller_stack_pos);
reloadStackFunctionCache();
return ret_values;
}
void OS::Core::opGetProperty(bool auto_create)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int ret_values = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(stack_values.count >= 2);
pushPropertyValue(stack_values.buf[stack_values.count - 2],
PropertyIndex(stack_values.buf[stack_values.count - 1]), true, true, true, auto_create);
removeStackValues(-3, 2);
// OS_ASSERT(ret_values == 1);
syncRetValues(ret_values, 1);
}
void OS::Core::opGetThisPropertyByString()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int ret_values = *stack_func->opcodes.cur++; // readByte();
int i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
OS_ASSERT(stack_func_prog_strings[i]->type == OS_VALUE_TYPE_STRING);
pushPropertyValue(stack_func->self,
PropertyIndex(stack_func_prog_strings[i], PropertyIndex::KeepStringIndex()), true, true, true, false);
// OS_ASSERT(ret_values == 1);
syncRetValues(ret_values, 1);
}
void OS::Core::opGetPropertyByLocals(bool auto_create)
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 3 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 3;
int ret_values = buf[0];
int local_1 = buf[1];
int local_2 = buf[2];
#else
int ret_values = stack_func->opcodes.readByte();
int local_1 = stack_func->opcodes.readByte();
int local_2 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
#if 1 // inline function for speed optimization
const bool anonymous_getter_enabled = true, named_getter_enabled = true, prototype_enabled = true;
Value * stack_func_locals = this->stack_func_locals;
Value table_value = stack_func_locals[local_1];
PropertyIndex index(stack_func_locals[local_2]);
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
break;
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
pushPropertyValueForPrimitive(table_value, index, anonymous_getter_enabled, named_getter_enabled, prototype_enabled, auto_create);
syncRetValues(ret_values, 1);
return;
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
for(GCValue * self = table_value.v.value;;){
#if 1 // inline function for speed optimization
for(;;){
if(self->type == OS_VALUE_TYPE_ARRAY && index.index.type == OS_VALUE_TYPE_NUMBER){
OS_ASSERT(dynamic_cast<GCArrayValue*>(self));
int i = (int)index.index.v.number;
if((i >= 0 || (i += ((GCArrayValue*)self)->values.count) >= 0) && i < ((GCArrayValue*)self)->values.count){
pushValue(((GCArrayValue*)self)->values[i]);
}else{
pushNull();
}
syncRetValues(ret_values, 1);
return;
}
Property * prop;
Table * table = self->table;
if(table && (prop = table->get(index))){
pushValue(prop->value);
syncRetValues(ret_values, 1);
return;
}
if(prototype_enabled){
GCValue * cur_value = self;
while(cur_value->prototype){
cur_value = cur_value->prototype;
Table * cur_table = cur_value->table;
if(cur_table && (prop = cur_table->get(index))){
pushValue(prop->value);
syncRetValues(ret_values, 1);
return;
}
}
}
if(index.index.type == OS_VALUE_TYPE_STRING && strings->syntax_prototype == index.index.v.string){
pushValue(self->prototype);
syncRetValues(ret_values, 1);
return;
}
if(self->type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<GCArrayValue*>(self));
OS_NUMBER number;
if(isValueNumber(index.index, &number)){
int i = (int)number;
if((i >= 0 || (i += ((GCArrayValue*)self)->values.count) >= 0) && i < ((GCArrayValue*)self)->values.count){
pushValue(((GCArrayValue*)self)->values[i]);
}else{
pushNull();
}
}else{
pushNull();
}
syncRetValues(ret_values, 1);
return;
}
break;
}
#else
Value value
if(getPropertyValue(value, table_value, index, prototype_enabled)){
pushValue(value);
syncRetValues(ret_values, 1);
return;
}
#endif
if(!hasSpecialPrefix(index.index)){
Value value;
if(index.index.type == OS_VALUE_TYPE_STRING){
const void * buf1 = strings->__getAt.toChar();
int size1 = strings->__getAt.getDataSize();
const void * buf2 = index.index.v.string->toChar();
int size2 = index.index.v.string->getDataSize();
GCStringValue * getter_name = newStringValue(buf1, size1, buf2, size2);
if(getPropertyValue(value, table_value, PropertyIndex(getter_name, PropertyIndex::KeepStringIndex()), prototype_enabled)){
pushValue(value);
pushValue(self);
call(0, 1);
syncRetValues(ret_values, 1);
return;
}
}
if(getPropertyValue(value, table_value, PropertyIndex(strings->__get, PropertyIndex::KeepStringIndex()), prototype_enabled)){
// auto_create = false;
if(value.type == OS_VALUE_TYPE_OBJECT){
table_value = value.v.value;
continue;
}
pushValue(value);
pushValue(self);
pushValue(index.index);
if(!auto_create){
call(1, 1);
}else{
pushBool(true);
call(2, 1);
}
if(auto_create && stack_values.lastElement().type == OS_VALUE_TYPE_NULL){
pop();
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
}
syncRetValues(ret_values, 1);
return;
}
}
if(auto_create){
setPropertyValue(self, index, Value(pushObjectValue()), false, false);
syncRetValues(ret_values, 1);
return;
}
break;
}
break;
}
pushNull();
#else
pushPropertyValue(stack_func_locals[local_1],
PropertyIndex(stack_func_locals[local_2]), true, true, true, auto_create);
#endif
// OS_ASSERT(ret_values == 1);
syncRetValues(ret_values, 1);
}
void OS::Core::opGetPropertyByLocalAndNumber(bool auto_create)
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int ret_values = buf[0];
int local_1 = buf[1];
#else
int ret_values = stack_func->opcodes.readByte();
int local_1 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals);
int number_index = stack_func->opcodes.readUVariable();
OS_ASSERT(number_index >= 0 && number_index < stack_func->func->prog->num_numbers);
pushPropertyValue(stack_func_locals[local_1],
PropertyIndex(stack_func_prog_numbers[number_index]), true, true, true, auto_create);
// OS_ASSERT(ret_values == 1);
syncRetValues(ret_values, 1);
}
void OS::Core::opSetProperty()
{
OS_ASSERT(stack_values.count >= 3);
setPropertyValue(stack_values.buf[stack_values.count - 2],
PropertyIndex(stack_values.buf[stack_values.count - 1]),
stack_values.buf[stack_values.count - 3], true, true);
pop(3);
}
void OS::Core::opSetPropertyByLocals(bool auto_create)
{
OS_ASSERT(stack_values.count >= 1);
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int local_1 = buf[0];
int local_2 = buf[1];
#else
int local_1 = stack_func->opcodes.readByte();
int local_2 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
if(auto_create && stack_func_locals[local_1].type == OS_VALUE_TYPE_NULL){
stack_func_locals[local_1] = newObjectValue();
}
setPropertyValue(stack_func_locals[local_1],
PropertyIndex(stack_func_locals[local_2]),
stack_values.lastElement(), true, true);
pop();
}
void OS::Core::opGetSetPropertyByLocals(bool auto_create)
{
OS_ASSERT(stack_values.count >= 1);
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 4 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 4;
int local_1 = buf[0];
int local_2 = buf[1];
#else
int local_1 = stack_func->opcodes.readByte();
int local_2 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
pushPropertyValue(stack_func_locals[local_1],
PropertyIndex(stack_func_locals[local_2]), true, true, true, auto_create);
#if 1 // speed optimization
local_1 = buf[2];
local_2 = buf[3];
#else
local_1 = stack_func->opcodes.readByte();
local_2 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
if(auto_create && stack_func_locals[local_1].type == OS_VALUE_TYPE_NULL){
stack_func_locals[local_1] = newObjectValue();
}
setPropertyValue(stack_func_locals[local_1],
PropertyIndex(stack_func_locals[local_2]),
stack_values.lastElement(), true, true);
pop();
}
void OS::Core::opSetDim()
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int params = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(stack_values.count >= 2 + params);
moveStackValue(-2-params, -1-params); // put value to the first param
params++;
Value table_value = stack_values[stack_values.count-1-params];
Value func;
if(getPropertyValue(func, table_value,
PropertyIndex(params == 1 ? strings->__setempty : strings->__setdim, PropertyIndex::KeepStringIndex()), true)
&& func.isFunction())
{
#if 1 // speed optimization
insertValue(func, -1-params);
call(params, 0, NULL, true);
#else
pushValue(func);
pushValue(table_value);
moveStackValues(-2, 2, -2-params); // put func value before params
call(params, 0);
removeStackValue(-1); // remove table_value
#endif
}else{
pop(params+1);
}
}
void OS::Core::opExtends()
{
OS_ASSERT(stack_values.count >= 2);
Value right_value = stack_values.lastElement();
switch(right_value.type){
case OS_VALUE_TYPE_NULL:
// null value has no prototype
break;
case OS_VALUE_TYPE_BOOL:
case OS_VALUE_TYPE_NUMBER:
break;
case OS_VALUE_TYPE_STRING:
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_FUNCTION:
right_value.v.value->prototype = stack_values[stack_values.count-2].getGCValue();
break;
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_CFUNCTION:
// TODO: warning???
break;
}
removeStackValue(-2);
}
void OS::Core::opClone()
{
OS_ASSERT(stack_values.count >= 1);
pushCloneValue(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opDeleteProperty()
{
OS_ASSERT(stack_values.count >= 2);
deleteValueProperty(stack_values[stack_values.count-2],
stack_values.lastElement(), true, true, false);
pop(2);
}
void OS::Core::opLogicAndOr1(bool is_and)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) != is_and){
// int offs = stack_func->opcodes.readInt16();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
int offs = (OS_INT8)stack_func->opcodes.cur[0];
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
// pop();
--stack_values.count;
}
}
void OS::Core::opLogicAndOr2(bool is_and)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) != is_and){
// int offs = stack_func->opcodes.readInt16();
OS_ASSERT(stack_func->opcodes.getPos()+2 <= stack_func->opcodes.getSize());
int offs = (OS_INT16)(stack_func->opcodes.cur[0] | (stack_func->opcodes.cur[1] << 8));
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
// pop();
--stack_values.count;
}
}
void OS::Core::opLogicAndOr4(bool is_and)
{
StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_values.count >= 1);
// Value value = stack_values.lastElement();
if(valueToBool(stack_values.lastElement()) != is_and){
int offs = stack_func->opcodes.readInt32();
// stack_func->opcodes.movePos(offs);
OS_ASSERT(stack_func->opcodes.getPos()+offs >= 0 && stack_func->opcodes.getPos()+offs <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += offs;
}else{
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) >= 0);
OS_ASSERT(stack_func->opcodes.getPos()+(int)sizeof(OS_INT32) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += sizeof(OS_INT32);
// pop();
--stack_values.count;
}
}
void OS::Core::opSuper()
{
StackFunction * stack_func = this->stack_func;
if(stack_func->self_for_proto){
GCValue * proto = stack_func->self_for_proto->prototype;
if(stack_func->self_for_proto->is_object_instance){
proto = proto ? proto->prototype : NULL;
}
pushValue(proto);
}else{
pushNull();
}
}
void OS::Core::opTypeOf()
{
OS_ASSERT(stack_values.count >= 1);
pushTypeOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opValueOf()
{
OS_ASSERT(stack_values.count >= 1);
pushValueOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opNumberOf()
{
OS_ASSERT(stack_values.count >= 1);
pushNumberOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opStringOf()
{
OS_ASSERT(stack_values.count >= 1);
pushStringOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opArrayOf()
{
OS_ASSERT(stack_values.count >= 1);
pushArrayOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opObjectOf()
{
OS_ASSERT(stack_values.count >= 1);
pushObjectOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opUserdataOf()
{
OS_ASSERT(stack_values.count >= 1);
pushUserdataOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opFunctionOf()
{
OS_ASSERT(stack_values.count >= 1);
pushFunctionOf(stack_values.lastElement());
removeStackValue(-2);
}
void OS::Core::opBooleanOf(bool b)
{
OS_ASSERT(stack_values.count >= 1);
stack_values.lastElement() = valueToBool(stack_values.lastElement()) == b;
}
void OS::Core::opIn()
{
OS_ASSERT(stack_values.count >= 2);
Core::GCValue * self = stack_values.lastElement().getGCValue();
bool has_property = self && hasProperty(self, stack_values[stack_values.count-2], true, true, true);
pop(2);
pushBool(has_property);
}
void OS::Core::opIsPrototypeOf()
{
OS_ASSERT(stack_values.count >= 2);
bool ret = isValuePrototypeOf(stack_values[stack_values.count-2], stack_values.lastElement());
pop(2);
pushBool(ret);
}
void OS::Core::opIs()
{
OS_ASSERT(stack_values.count >= 2);
bool ret = isValueInstanceOf(stack_values[stack_values.count-2], stack_values.lastElement());
pop(2);
pushBool(ret);
}
void OS::Core::opLength()
{
OS_ASSERT(stack_values.count >= 1);
Value value = stack_values.lastElement();
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, value,
PropertyIndex(strings->__len, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
pushValue(func);
pushValue(value);
call(0, 1);
}else{
pushNull();
}
removeStackValue(-2);
}
void OS::Core::opUnaryOperator(int opcode)
{
OS_ASSERT(stack_values.count >= 1);
pushOpResultValue(opcode, stack_values.lastElement());
removeStackValue(-2);
}
/*
void OS::Core::opBinaryOperator(int opcode)
{
OS_ASSERT(stack_values.count >= 2);
pushOpResultValue(opcode, stack_values[stack_values.count-2], stack_values.lastElement());
removeStackValues(-3, 2);
}
*/
void OS::Core::opBinaryOperatorByLocals()
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 3 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 3;
int opcode = buf[0];
int local_1 = buf[1];
int local_2 = buf[2];
#else
int opcode = stack_func->opcodes.readByte();
int local_1 = stack_func->opcodes.readByte();
int local_2 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
#if 0 // increase in speed is not detected
Value * stack_func_locals = this->stack_func_locals;
const Value& left_value = stack_func_locals[local_1];
const Value& right_value = stack_func_locals[local_2];
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
StackValues * stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
OS_NUMBER right_num;
switch(opcode){
case Program::OP_COMPARE:
stack_values->buf[stack_values->count++] = left_value.v.number - right_value.v.number;
return;
case Program::OP_LOGIC_EQ:
stack_values->buf[stack_values->count++] = left_value.v.number == right_value.v.number;
return;
case Program::OP_LOGIC_NE:
stack_values->buf[stack_values->count++] = left_value.v.number != right_value.v.number;
return;
case Program::OP_LOGIC_GE:
stack_values->buf[stack_values->count++] = left_value.v.number >= right_value.v.number;
return;
case Program::OP_LOGIC_LE:
stack_values->buf[stack_values->count++] = left_value.v.number <= right_value.v.number;
return;
case Program::OP_LOGIC_GREATER:
stack_values->buf[stack_values->count++] = left_value.v.number > right_value.v.number;
return;
case Program::OP_LOGIC_LESS:
stack_values->buf[stack_values->count++] = left_value.v.number < right_value.v.number;
return;
case Program::OP_BIT_AND:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number & (OS_INT)right_value.v.number;
return;
case Program::OP_BIT_OR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number | (OS_INT)right_value.v.number;
return;
case Program::OP_BIT_XOR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number ^ (OS_INT)right_value.v.number;
return;
case Program::OP_ADD: // +
stack_values->buf[stack_values->count++] = left_value.v.number + right_value.v.number;
return;
case Program::OP_SUB: // -
stack_values->buf[stack_values->count++] = left_value.v.number - right_value.v.number;
return;
case Program::OP_MUL: // *
stack_values->buf[stack_values->count++] = left_value.v.number * right_value.v.number;
return;
case Program::OP_DIV: // /
right_num = right_value.v.number;
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = left_value.v.number / right_num;
}
return;
case Program::OP_MOD: // %
right_num = right_value.v.number;
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = OS_MATH_MOD_OPERATOR(left_value.v.number, right_num);
}
return;
case Program::OP_LSHIFT: // <<
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number << (OS_INT)right_value.v.number;
return;
case Program::OP_RSHIFT: // >>
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number >> (OS_INT)right_value.v.number;
return;
case Program::OP_POW: // **
stack_values->buf[stack_values->count++] = OS_MATH_POW_OPERATOR((OS_FLOAT)left_value.v.number, (OS_FLOAT)right_value.v.number);
return;
}
}
pushOpResultValue(opcode, left_value, right_value);
#else
pushOpResultValue(opcode, stack_func_locals[local_1], stack_func_locals[local_2]);
#endif
}
void OS::Core::opBinaryOperatorByLocalAndNumber()
{
StackFunction * stack_func = this->stack_func;
#if 1 // speed optimization
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int opcode = buf[0];
int local_1 = buf[1];
#else
int opcode = stack_func->opcodes.readByte();
int local_1 = stack_func->opcodes.readByte();
#endif
OS_ASSERT(local_1 < num_stack_func_locals);
int number_index = stack_func->opcodes.readUVariable();
OS_ASSERT(number_index >= 0 && number_index < stack_func->func->prog->num_numbers);
#if 1 // inline function for speed optimization
const Value& left_value = stack_func_locals[local_1];
if(left_value.type == OS_VALUE_TYPE_NUMBER){
StackValues * stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
OS_NUMBER right_num;
switch(opcode){
case Program::OP_COMPARE:
stack_values->buf[stack_values->count++] = left_value.v.number - stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_EQ:
stack_values->buf[stack_values->count++] = left_value.v.number == stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_NE:
stack_values->buf[stack_values->count++] = left_value.v.number != stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_GE:
stack_values->buf[stack_values->count++] = left_value.v.number >= stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_LE:
stack_values->buf[stack_values->count++] = left_value.v.number <= stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_GREATER:
stack_values->buf[stack_values->count++] = left_value.v.number > stack_func_prog_numbers[number_index];
return;
case Program::OP_LOGIC_LESS:
stack_values->buf[stack_values->count++] = left_value.v.number < stack_func_prog_numbers[number_index];
return;
case Program::OP_BIT_AND:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number & (OS_INT)stack_func_prog_numbers[number_index];
return;
case Program::OP_BIT_OR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number | (OS_INT)stack_func_prog_numbers[number_index];
return;
case Program::OP_BIT_XOR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number ^ (OS_INT)stack_func_prog_numbers[number_index];
return;
case Program::OP_ADD: // +
stack_values->buf[stack_values->count++] = left_value.v.number + stack_func_prog_numbers[number_index];
return;
case Program::OP_SUB: // -
stack_values->buf[stack_values->count++] = left_value.v.number - stack_func_prog_numbers[number_index];
return;
case Program::OP_MUL: // *
stack_values->buf[stack_values->count++] = left_value.v.number * stack_func_prog_numbers[number_index];
return;
case Program::OP_DIV: // /
right_num = stack_func_prog_numbers[number_index];
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = left_value.v.number / right_num;
}
return;
case Program::OP_MOD: // %
right_num = stack_func_prog_numbers[number_index];
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = OS_MATH_MOD_OPERATOR(left_value.v.number, right_num);
}
return;
case Program::OP_LSHIFT: // <<
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number << (OS_INT)stack_func_prog_numbers[number_index];
return;
case Program::OP_RSHIFT: // >>
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number >> (OS_INT)stack_func_prog_numbers[number_index];
return;
case Program::OP_POW: // **
stack_values->buf[stack_values->count++] = OS_MATH_POW_OPERATOR((OS_FLOAT)left_value.v.number, (OS_FLOAT)stack_func_prog_numbers[number_index]);
return;
}
}
#endif
pushOpResultValue(opcode, stack_func_locals[local_1], stack_func_prog_numbers[number_index]);
}
void OS::Core::reloadStackFunctionCache()
{
if(call_stack_funcs.count > 0){
stack_func = &call_stack_funcs.lastElement();
stack_func_locals = stack_func->locals->locals;
num_stack_func_locals = stack_func->locals->num_locals;
stack_func_env_index = stack_func->func->func_decl->num_params + ENV_VAR_INDEX;
stack_func_prog_numbers = stack_func->func->prog->const_numbers;
stack_func_prog_strings = stack_func->func->prog->const_strings;
}else{
stack_func = NULL;
stack_func_locals = NULL;
num_stack_func_locals = 0;
stack_func_env_index = 0;
stack_func_prog_numbers = NULL;
stack_func_prog_strings = NULL;
}
}
int OS::Core::execute()
{
#ifdef OS_DEBUG
allocator->checkNativeStackUsage(OS_TEXT("OS::Core::execute"));
#endif
StackFunction * stack_func;
StackValues * stack_values;
OS_NUMBER right_num;
int i, ret_values, ret_stack_funcs = call_stack_funcs.count-1;
#ifdef OS_INFINITE_LOOP_OPCODES
for(int opcodes_executed = 0;; opcodes_executed++){
#else
for(;;){
#endif
// StackFunction * stack_func = &call_stack_funcs.lastElement(); // could be invalid because of stack resize
OS_ASSERT(this->stack_values.count >= this->stack_func->stack_pos);
// stack_func->opcode_offs = opcodes.pos; // used by debugger to show currect position if debug info present
if(terminated
#ifdef OS_INFINITE_LOOP_OPCODES
|| opcodes_executed >= OS_INFINITE_LOOP_OPCODES
#endif
)
{
break;
}
OS_ASSERT(this->stack_func->opcodes.getPos()+1 <= this->stack_func->opcodes.getSize());
Program::OpcodeType opcode = (Program::OpcodeType)*(stack_func = this->stack_func)->opcodes.cur++; // readByte();
OS_PROFILE_BEGIN_OPCODE(opcode);
switch(opcode){
default:
error(OS_E_ERROR, "Unknown opcode, program is corrupted!!!");
allocator->setTerminated();
break;
case Program::OP_DEBUGGER:
opDebugger();
break;
case Program::OP_PUSH_ONE:
//opPushNumber();
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
stack_values->buf[stack_values->count++] = 1.0f;
break;
case Program::OP_PUSH_NUMBER_1:
//opPushNumber();
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
i = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_numbers);
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
stack_values->buf[stack_values->count++] = stack_func_prog_numbers[i];
// pushNumber(stack_func_prog_numbers[i]);
break;
case Program::OP_PUSH_NUMBER_BY_AUTO_INDEX:
//opPushNumber();
i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_numbers);
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
stack_values->buf[stack_values->count++] = stack_func_prog_numbers[i];
// pushNumber(stack_func_prog_numbers[i]);
break;
case Program::OP_PUSH_STRING_1:
{
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
i = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
OS_ASSERT(stack_func_prog_strings[i]->type == OS_VALUE_TYPE_STRING);
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
Value& value = stack_values->buf[stack_values->count++];
value.v.string = stack_func_prog_strings[i];
value.type = OS_VALUE_TYPE_STRING;
// opPushString();
break;
}
case Program::OP_PUSH_STRING_BY_AUTO_INDEX:
{
i = stack_func->opcodes.readUVariable();
OS_ASSERT(i >= 0 && i < stack_func->func->prog->num_strings);
OS_ASSERT(stack_func_prog_strings[i]->type == OS_VALUE_TYPE_STRING);
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
Value& value = stack_values->buf[stack_values->count++];
value.v.string = stack_func_prog_strings[i];
value.type = OS_VALUE_TYPE_STRING;
// opPushString();
break;
}
case Program::OP_PUSH_NULL:
pushNull();
break;
case Program::OP_PUSH_TRUE:
pushBool(true);
break;
case Program::OP_PUSH_FALSE:
pushBool(false);
break;
case Program::OP_PUSH_FUNCTION:
opPushFunction();
break;
case Program::OP_PUSH_NEW_ARRAY:
opPushArray();
break;
case Program::OP_PUSH_NEW_OBJECT:
opPushObject();
break;
case Program::OP_OBJECT_SET_BY_AUTO_INDEX:
opObjectSetByAutoIndex();
break;
case Program::OP_OBJECT_SET_BY_EXP:
opObjectSetByExp();
break;
case Program::OP_OBJECT_SET_BY_INDEX:
opObjectSetByIndex();
break;
case Program::OP_OBJECT_SET_BY_NAME:
opObjectSetByName();
break;
case Program::OP_PUSH_ENV_VAR:
case Program::OP_PUSH_ENV_VAR_AUTO_CREATE:
opPushEnvVar(opcode == Program::OP_PUSH_ENV_VAR_AUTO_CREATE);
break;
case Program::OP_SET_ENV_VAR:
opSetEnvVar();
break;
case Program::OP_PUSH_THIS:
opPushThis();
break;
case Program::OP_PUSH_ARGUMENTS:
opPushArguments();
break;
case Program::OP_PUSH_REST_ARGUMENTS:
opPushRestArguments();
break;
case Program::OP_PUSH_LOCAL_VAR_1:
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
i = *stack_func->opcodes.cur++; // readByte();
OS_ASSERT(i < num_stack_func_locals);
#if 1 // inline function for speed optimization
stack_values = &this->stack_values;
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
stack_values->buf[stack_values->count++] = stack_func_locals[i];
#else
pushValue(stack_func_locals[i]);
#endif
break;
case Program::OP_PUSH_LOCAL_VAR_BY_AUTO_INDEX:
opPushLocalVar();
break;
case Program::OP_PUSH_LOCAL_VAR_AUTO_CREATE:
opPushLocalVarAutoCreate();
break;
case Program::OP_SET_LOCAL_VAR_1:
// inline function for speed optimization
{
// StackFunction * stack_func = this->stack_func;
OS_ASSERT(this->stack_values.count >= 1);
OS_ASSERT(stack_func->opcodes.getPos()+1 <= stack_func->opcodes.getSize());
i = *stack_func->opcodes.cur++; // readByte();
// Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(i < num_stack_func_locals);
stack_values = &this->stack_values;
switch((stack_func_locals[i] = stack_values->buf[--stack_values->count]).type){
case OS_VALUE_TYPE_FUNCTION:
OS_ASSERT(dynamic_cast<GCFunctionValue*>(stack_func_locals[i].v.func));
if(!stack_func_locals[i].v.func->name){
stack_func_locals[i].v.func->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
case OS_VALUE_TYPE_CFUNCTION:
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(stack_func_locals[i].v.cfunc));
if(!stack_func_locals[i].v.cfunc->name){
stack_func_locals[i].v.cfunc->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
}
// already removed
// pop();
break;
}
case Program::OP_SET_LOCAL_VAR:
opSetLocalVar();
break;
case Program::OP_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCALS:
opBinaryOperatorByLocals();
opSetLocalVar();
break;
case Program::OP_SET_LOCAL_VAR_1_BY_BIN_OPERATOR_LOCAL_AND_NUMBER:
// inline function for speed optimization
{
// StackFunction * stack_func = this->stack_func;
OS_ASSERT(stack_func->opcodes.getPos() + 4 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 4;
int opcode = buf[0];
int local_1 = buf[1];
OS_ASSERT(local_1 < num_stack_func_locals);
int number_index = buf[2]; // stack_func->opcodes.readUVariable();
OS_ASSERT(number_index >= 0 && number_index < stack_func->func->prog->num_numbers);
stack_values = &this->stack_values;
const Value& left_value = stack_func_locals[local_1];
if(left_value.type == OS_VALUE_TYPE_NUMBER){
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
switch(opcode){
case Program::OP_COMPARE:
stack_values->buf[stack_values->count++] = left_value.v.number - stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_EQ:
stack_values->buf[stack_values->count++] = left_value.v.number == stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_NE:
stack_values->buf[stack_values->count++] = left_value.v.number != stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_GE:
stack_values->buf[stack_values->count++] = left_value.v.number >= stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_LE:
stack_values->buf[stack_values->count++] = left_value.v.number <= stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_GREATER:
stack_values->buf[stack_values->count++] = left_value.v.number > stack_func_prog_numbers[number_index];
break;
case Program::OP_LOGIC_LESS:
stack_values->buf[stack_values->count++] = left_value.v.number < stack_func_prog_numbers[number_index];
break;
case Program::OP_BIT_AND:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number & (OS_INT)stack_func_prog_numbers[number_index];
break;
case Program::OP_BIT_OR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number | (OS_INT)stack_func_prog_numbers[number_index];
break;
case Program::OP_BIT_XOR:
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number ^ (OS_INT)stack_func_prog_numbers[number_index];
break;
case Program::OP_ADD: // +
stack_values->buf[stack_values->count++] = left_value.v.number + stack_func_prog_numbers[number_index];
break;
case Program::OP_SUB: // -
stack_values->buf[stack_values->count++] = left_value.v.number - stack_func_prog_numbers[number_index];
break;
case Program::OP_MUL: // *
stack_values->buf[stack_values->count++] = left_value.v.number * stack_func_prog_numbers[number_index];
break;
case Program::OP_DIV: // /
right_num = stack_func_prog_numbers[number_index];
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = left_value.v.number / right_num;
}
break;
case Program::OP_MOD: // %
right_num = stack_func_prog_numbers[number_index];
if(!right_num){
errorDivisionByZero();
stack_values->buf[stack_values->count++] = 0.0;
}else{
stack_values->buf[stack_values->count++] = OS_MATH_MOD_OPERATOR(left_value.v.number, right_num);
}
break;
case Program::OP_LSHIFT: // <<
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number << (OS_INT)stack_func_prog_numbers[number_index];
break;
case Program::OP_RSHIFT: // >>
stack_values->buf[stack_values->count++] = (OS_INT)left_value.v.number >> (OS_INT)stack_func_prog_numbers[number_index];
break;
case Program::OP_POW: // **
stack_values->buf[stack_values->count++] = OS_MATH_POW_OPERATOR((OS_FLOAT)left_value.v.number, (OS_FLOAT)stack_func_prog_numbers[number_index]);
break;
default:
pushOpResultValue(opcode, stack_func_locals[local_1], stack_func_prog_numbers[number_index]);
}
}else{
pushOpResultValue(opcode, stack_func_locals[local_1], stack_func_prog_numbers[number_index]);
}
// opSetLocalVar();
OS_ASSERT(this->stack_values.count >= 1);
i = buf[3];
// Upvalues * func_upvalues = stack_func->locals;
OS_ASSERT(i < num_stack_func_locals);
// stack_values = &this->stack_values;
switch((stack_func_locals[i] = stack_values->buf[--stack_values->count]).type){
case OS_VALUE_TYPE_FUNCTION:
OS_ASSERT(dynamic_cast<GCFunctionValue*>(stack_func_locals[i].v.func));
if(!stack_func_locals[i].v.func->name){
stack_func_locals[i].v.func->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
case OS_VALUE_TYPE_CFUNCTION:
OS_ASSERT(dynamic_cast<GCCFunctionValue*>(stack_func_locals[i].v.cfunc));
if(!stack_func_locals[i].v.cfunc->name){
stack_func_locals[i].v.cfunc->name = stack_func->func->func_decl->locals[i].name.string;
}
break;
}
// already removed
// pop();
break;
}
case Program::OP_SET_LOCAL_VAR_BY_BIN_OPERATOR_LOCAL_AND_NUMBER:
opBinaryOperatorByLocalAndNumber();
opSetLocalVar();
break;
case Program::OP_PUSH_UP_LOCAL_VAR:
opPushUpvalue();
break;
case Program::OP_PUSH_UP_LOCAL_VAR_AUTO_CREATE:
opPushUpvalueAutoCreate();
break;
case Program::OP_SET_UP_LOCAL_VAR:
opSetUpvalue();
break;
case Program::OP_IF_NOT_JUMP_1:
opIfJump1(false);
break;
case Program::OP_IF_NOT_JUMP_2:
opIfJump2(false);
break;
case Program::OP_IF_NOT_JUMP_4:
opIfJump4(false);
break;
case Program::OP_IF_JUMP_1:
opIfJump1(true);
break;
case Program::OP_IF_JUMP_2:
opIfJump2(true);
break;
case Program::OP_IF_JUMP_4:
opIfJump4(true);
break;
case Program::OP_JUMP_1:
OS_ASSERT(stack_func->opcodes.getPos() + *(OS_INT8*)stack_func->opcodes.cur >= 0);
OS_ASSERT(stack_func->opcodes.getPos() + *(OS_INT8*)stack_func->opcodes.cur <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += *(OS_INT8*)stack_func->opcodes.cur;
break;
case Program::OP_JUMP_2:
OS_ASSERT(stack_func->opcodes.getPos() + (OS_INT16)(stack_func->opcodes.cur[0] | (stack_func->opcodes.cur[1] << 8)) >= 0);
OS_ASSERT(stack_func->opcodes.getPos() + (OS_INT16)(stack_func->opcodes.cur[0] | (stack_func->opcodes.cur[1] << 8)) <= stack_func->opcodes.getSize());
stack_func->opcodes.cur += (OS_INT16)(stack_func->opcodes.cur[0] | (stack_func->opcodes.cur[1] << 8));
break;
case Program::OP_JUMP_4:
opJump4();
break;
case Program::OP_CALL:
opCall();
break;
case Program::OP_SUPER_CALL:
OS_PROFILE_END_OPCODE(opcode); // we shouldn't profile call here
opSuperCall(ret_values);
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
return ret_values;
}
continue;
case Program::OP_TAIL_CALL:
OS_PROFILE_END_OPCODE(opcode); // we shouldn't profile call here
opTailCall(ret_values);
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
return ret_values;
}
continue;
case Program::OP_CALL_METHOD:
OS_PROFILE_END_OPCODE(opcode); // we shouldn't profile call here
opCallMethod();
continue;
case Program::OP_TAIL_CALL_METHOD:
OS_PROFILE_END_OPCODE(opcode); // we shouldn't profile call here
opTailCallMethod(ret_values);
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
return ret_values;
}
continue;
case Program::OP_RETURN:
ret_values = opReturn();
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
OS_PROFILE_END_OPCODE(opcode);
return ret_values;
}
break;
case Program::OP_RETURN_AUTO:
ret_values = opReturnAuto();
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
OS_PROFILE_END_OPCODE(opcode);
return ret_values;
}
break;
case Program::OP_GET_PROPERTY:
opGetProperty(false);
break;
case Program::OP_GET_THIS_PROPERTY_BY_STRING:
opGetThisPropertyByString();
break;
case Program::OP_GET_PROPERTY_AUTO_CREATE:
opGetProperty(true);
break;
case Program::OP_GET_PROPERTY_BY_LOCALS:
opGetPropertyByLocals(false);
break;
case Program::OP_GET_PROPERTY_BY_LOCAL_AND_NUMBER:
opGetPropertyByLocalAndNumber(false);
break;
case Program::OP_SET_PROPERTY:
#if 1 // inline function for speed optimization
OS_ASSERT(this->stack_values.count >= 3);
stack_values = &this->stack_values;
setPropertyValue(stack_values->buf[stack_values->count - 2],
PropertyIndex(stack_values->buf[stack_values->count - 1]),
stack_values->buf[stack_values->count - 3], true, true);
OS_ASSERT(this->stack_values.count >= 3);
// pop(3);
stack_values->count -= 3;
#else
opSetProperty();
#endif
break;
case Program::OP_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
{
#if 0 // increase in speed is not detected
OS_ASSERT(this->stack_values.count >= 1);
OS_ASSERT(stack_func->opcodes.getPos() + 2 <= stack_func->opcodes.size);
OS_BYTE * buf = stack_func->opcodes.cur;
stack_func->opcodes.cur += 2;
int local_1 = buf[0];
int local_2 = buf[1];
OS_ASSERT(local_1 < num_stack_func_locals && local_2 < num_stack_func_locals);
Value * stack_func_locals = this->stack_func_locals;
if(stack_func_locals[local_1].type == OS_VALUE_TYPE_NULL){
stack_func_locals[local_1] = newObjectValue();
}
//setPropertyValue(stack_func_locals[local_1], PropertyIndex(stack_func_locals[local_2]), stack_values.lastElement(), true, true);
PropertyIndex index(stack_func_locals[local_2]);
Value value = stack_values->buf[--stack_values->count];
Value table_value = stack_func_locals[local_1];
switch(table_value.type){
case OS_VALUE_TYPE_NULL:
break;
case OS_VALUE_TYPE_BOOL:
// return setPropertyValue(prototypes[PROTOTYPE_BOOL], index, value, setter_enabled);
break;
case OS_VALUE_TYPE_NUMBER:
// return setPropertyValue(prototypes[PROTOTYPE_NUMBER], index, value, setter_enabled);
break;
case OS_VALUE_TYPE_STRING:
// return setPropertyValue(prototypes[PROTOTYPE_STRING], index, value, setter_enabled);
// return;
case OS_VALUE_TYPE_ARRAY:
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
setPropertyValue(table_value.v.value, index, value, true, true);
break;
}
// already removed
// pop();
#else
opSetPropertyByLocals(true);
#endif
break;
}
case Program::OP_GET_SET_PROPERTY_BY_LOCALS_AUTO_CREATE:
opGetSetPropertyByLocals(true);
break;
case Program::OP_SET_DIM:
opSetDim();
break;
case Program::OP_EXTENDS:
opExtends();
break;
case Program::OP_CLONE:
opClone();
break;
case Program::OP_DELETE_PROP:
opDeleteProperty();
break;
case Program::OP_POP:
// pop();
OS_ASSERT(this->stack_values.count > 0);
--this->stack_values.count;
break;
case Program::OP_LOGIC_AND_1:
opLogicAndOr1(true);
break;
case Program::OP_LOGIC_AND_2:
opLogicAndOr2(true);
break;
case Program::OP_LOGIC_AND_4:
opLogicAndOr4(true);
break;
case Program::OP_LOGIC_OR_1:
opLogicAndOr1(false);
break;
case Program::OP_LOGIC_OR_2:
opLogicAndOr2(false);
break;
case Program::OP_LOGIC_OR_4:
opLogicAndOr4(false);
break;
case Program::OP_SUPER:
opSuper();
break;
case Program::OP_TYPE_OF:
opTypeOf();
break;
case Program::OP_VALUE_OF:
opValueOf();
break;
case Program::OP_NUMBER_OF:
opNumberOf();
break;
case Program::OP_STRING_OF:
opStringOf();
break;
case Program::OP_ARRAY_OF:
opArrayOf();
break;
case Program::OP_OBJECT_OF:
opObjectOf();
break;
case Program::OP_USERDATA_OF:
opUserdataOf();
break;
case Program::OP_FUNCTION_OF:
opFunctionOf();
break;
case Program::OP_LOGIC_BOOL:
case Program::OP_LOGIC_NOT:
opBooleanOf(opcode == Program::OP_LOGIC_BOOL);
break;
case Program::OP_IN:
opIn();
break;
case Program::OP_ISPROTOTYPEOF:
opIsPrototypeOf();
break;
case Program::OP_IS:
opIs();
break;
case Program::OP_LENGTH:
opLength();
break;
case Program::OP_BIT_NOT:
case Program::OP_PLUS:
case Program::OP_NEG:
opUnaryOperator(opcode);
break;
case Program::OP_BIN_OPERATOR_BY_LOCALS:
opBinaryOperatorByLocals();
break;
case Program::OP_BIN_OPERATOR_BY_LOCAL_AND_NUMBER:
opBinaryOperatorByLocalAndNumber();
break;
case Program::OP_CONCAT:
case Program::OP_LOGIC_PTR_EQ:
case Program::OP_LOGIC_PTR_NE:
case Program::OP_LOGIC_EQ:
case Program::OP_LOGIC_NE:
case Program::OP_LOGIC_GE:
case Program::OP_LOGIC_LE:
case Program::OP_LOGIC_GREATER:
case Program::OP_LOGIC_LESS:
case Program::OP_BIT_AND:
case Program::OP_BIT_OR:
case Program::OP_BIT_XOR:
case Program::OP_ADD: // +
case Program::OP_SUB: // -
case Program::OP_MUL: // *
case Program::OP_DIV: // /
case Program::OP_MOD: // %
case Program::OP_LSHIFT: // <<
case Program::OP_RSHIFT: // >>
case Program::OP_POW: // **
// opBinaryOperator(opcode);
{
OS_ASSERT(this->stack_values.count >= 2);
stack_values = &this->stack_values;
#if 1 // speed optimization
const Value& left_value = stack_values->buf[stack_values->count-2];
const Value& right_value = stack_values->buf[stack_values->count-1];
if(left_value.type == OS_VALUE_TYPE_NUMBER && right_value.type == OS_VALUE_TYPE_NUMBER){
if(stack_values->capacity < stack_values->count+1){
reserveStackValues(stack_values->count+1);
}
switch(opcode){
case Program::OP_COMPARE:
stack_values->buf[--stack_values->count - 1] = left_value.v.number - right_value.v.number;
break;
case Program::OP_LOGIC_EQ:
stack_values->buf[--stack_values->count - 1] = left_value.v.number == right_value.v.number;
break;
case Program::OP_LOGIC_NE:
stack_values->buf[--stack_values->count - 1] = left_value.v.number != right_value.v.number;
break;
case Program::OP_LOGIC_GE:
stack_values->buf[--stack_values->count - 1] = left_value.v.number >= right_value.v.number;
break;
case Program::OP_LOGIC_LE:
stack_values->buf[--stack_values->count - 1] = left_value.v.number <= right_value.v.number;
break;
case Program::OP_LOGIC_GREATER:
stack_values->buf[--stack_values->count - 1] = left_value.v.number > right_value.v.number;
break;
case Program::OP_LOGIC_LESS:
stack_values->buf[--stack_values->count - 1] = left_value.v.number < right_value.v.number;
break;
case Program::OP_BIT_AND:
stack_values->buf[--stack_values->count - 1] = (OS_INT)left_value.v.number & (OS_INT)right_value.v.number;
break;
case Program::OP_BIT_OR:
stack_values->buf[--stack_values->count - 1] = (OS_INT)left_value.v.number | (OS_INT)right_value.v.number;
break;
case Program::OP_BIT_XOR:
stack_values->buf[--stack_values->count - 1] = (OS_INT)left_value.v.number ^ (OS_INT)right_value.v.number;
break;
case Program::OP_ADD: // +
stack_values->buf[--stack_values->count - 1] = left_value.v.number + right_value.v.number;
break;
case Program::OP_SUB: // -
stack_values->buf[--stack_values->count - 1] = left_value.v.number - right_value.v.number;
break;
case Program::OP_MUL: // *
stack_values->buf[--stack_values->count - 1] = left_value.v.number * right_value.v.number;
break;
case Program::OP_DIV: // /
right_num = right_value.v.number;
if(!right_num){
errorDivisionByZero();
stack_values->buf[--stack_values->count - 1] = 0.0;
}else{
stack_values->buf[--stack_values->count - 1] = left_value.v.number / right_num;
}
break;
case Program::OP_MOD: // %
right_num = right_value.v.number;
if(!right_num){
errorDivisionByZero();
stack_values->buf[--stack_values->count - 1] = 0.0;
}else{
stack_values->buf[--stack_values->count - 1] = OS_MATH_MOD_OPERATOR(left_value.v.number, right_num);
}
break;
case Program::OP_LSHIFT: // <<
stack_values->buf[--stack_values->count - 1] = (OS_INT)left_value.v.number << (OS_INT)right_value.v.number;
break;
case Program::OP_RSHIFT: // >>
stack_values->buf[--stack_values->count - 1] = (OS_INT)left_value.v.number >> (OS_INT)right_value.v.number;
break;
case Program::OP_POW: // **
stack_values->buf[--stack_values->count - 1] = OS_MATH_POW_OPERATOR((OS_FLOAT)left_value.v.number, (OS_FLOAT)right_value.v.number);
break;
default:
goto generic_bin_op;
}
break;
}
generic_bin_op:
pushOpResultValue(opcode, left_value, right_value);
#else
pushOpResultValue(opcode, stack_values->buf[stack_values->count-2], stack_values->buf[stack_values->count-1]);
#endif
OS_ASSERT(this->stack_values.count >= 3);
stack_values->buf[stack_values->count-3] = stack_values->buf[stack_values->count-1];
stack_values->count -= 2;
// removeStackValues(-3, 2);
break;
}
}
OS_PROFILE_END_OPCODE(opcode);
}
for(;;){
ret_values = opBreakFunction();
if(ret_stack_funcs >= call_stack_funcs.count){
OS_ASSERT(ret_stack_funcs == call_stack_funcs.count);
return ret_values;
}
}
return 0;
}
void OS::runOp(OS_EOpcode opcode)
{
struct Lib
{
Core * core;
void runBinaryOpcode(int opcode)
{
int count = core->stack_values.count;
if(count < 2){
core->pushNull();
return;
}
Core::Value left_value = core->stack_values[count-2];
Core::Value right_value = core->stack_values[count-1];
// core->stack_values.count -= 2;
core->pushOpResultValue(opcode, left_value, right_value);
core->removeStackValues(-3, 2);
}
void runUnaryOpcode(int opcode)
{
int count = core->stack_values.count;
if(count < 1){
core->pushNull();
return;
}
Core::Value value = core->stack_values[count-1];
core->pushOpResultValue(opcode, value);
core->removeStackValue(-2);
}
} lib = {core};
switch(opcode){
case OP_COMPARE:
return lib.runBinaryOpcode(Core::Program::OP_COMPARE);
case OP_LOGIC_PTR_EQ: // ===
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_PTR_EQ);
case OP_LOGIC_PTR_NE: // !==
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_PTR_NE);
case OP_LOGIC_EQ: // ==
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_EQ);
case OP_LOGIC_NE: // !=
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_NE);
case OP_LOGIC_GE: // >=
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_GE);
case OP_LOGIC_LE: // <=
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_LE);
case OP_LOGIC_GREATER: // >
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_GREATER);
case OP_LOGIC_LESS: // <
return lib.runBinaryOpcode(Core::Program::OP_LOGIC_LESS);
case OP_BIT_AND: // &
return lib.runBinaryOpcode(Core::Program::OP_BIT_AND);
case OP_BIT_OR: // |
return lib.runBinaryOpcode(Core::Program::OP_BIT_OR);
case OP_BIT_XOR: // ^
return lib.runBinaryOpcode(Core::Program::OP_BIT_XOR);
case OP_ADD: // +
return lib.runBinaryOpcode(Core::Program::OP_ADD);
case OP_SUB: // -
return lib.runBinaryOpcode(Core::Program::OP_SUB);
case OP_MUL: // *
return lib.runBinaryOpcode(Core::Program::OP_MUL);
case OP_DIV: // /
return lib.runBinaryOpcode(Core::Program::OP_DIV);
case OP_MOD: // %
return lib.runBinaryOpcode(Core::Program::OP_MOD);
case OP_LSHIFT: // <<
return lib.runBinaryOpcode(Core::Program::OP_LSHIFT);
case OP_RSHIFT: // >>
return lib.runBinaryOpcode(Core::Program::OP_RSHIFT);
case OP_POW: // **
return lib.runBinaryOpcode(Core::Program::OP_POW);
case OP_CONCAT: // ..
return lib.runBinaryOpcode(Core::Program::OP_CONCAT);
case OP_BIT_NOT: // ~
return lib.runUnaryOpcode(Core::Program::OP_BIT_NOT);
case OP_PLUS: // +
return lib.runUnaryOpcode(Core::Program::OP_PLUS);
case OP_NEG: // -
return lib.runUnaryOpcode(Core::Program::OP_NEG);
case OP_LENGTH: // #
return lib.runUnaryOpcode(Core::Program::OP_LENGTH);
/*
case OP_LOGIC_BOOL:
return lib.runUnaryOpcode(Core::Program::OP_LOGIC_BOOL);
case OP_LOGIC_NOT:
return lib.runUnaryOpcode(Core::Program::OP_LOGIC_NOT);
case OP_VALUE_OF:
return lib.runUnaryOpcode(Core::Program::OP_VALUE_OF);
case OP_NUMBER_OF:
return lib.runUnaryOpcode(Core::Program::OP_NUMBER_OF);
case OP_STRING_OF:
return lib.runUnaryOpcode(Core::Program::OP_STRING_OF);
case OP_ARRAY_OF:
return lib.runUnaryOpcode(Core::Program::OP_ARRAY_OF);
case OP_OBJECT_OF:
return lib.runUnaryOpcode(Core::Program::OP_OBJECT_OF);
case OP_USERDATA_OF:
return lib.runUnaryOpcode(Core::Program::OP_USERDATA_OF);
case OP_FUNCTION_OF:
return lib.runUnaryOpcode(Core::Program::OP_FUNCTION_OF);
case OP_CLONE:
return lib.runUnaryOpcode(Core::Program::OP_CLONE);
*/
}
pushNull();
}
int OS::getLen(int offs)
{
pushStackValue(offs);
runOp(OP_LENGTH);
return popInt();
}
void OS::getErrorHandler(int code)
{
for(int i = 0; i < OS_ERROR_LEVELS; i++){
if(code & (1<<i)){
core->pushValue(core->error_handlers[i]);
return;
}
}
pushNull();
}
void OS::setErrorHandler(int code)
{
bool returned = false;
Core::Value func = core->getStackValue(-1);
if(func.isFunction()){
for(int i = 0; i < OS_ERROR_LEVELS; i++){
if(code & (1<<i)){
if(!returned){
core->pushValue(core->error_handlers[i]);
returned = true;
}
core->error_handlers[i] = func;
}
}
}
if(!returned){
pushNull();
}
remove(-2);
}
void OS::setFunc(const FuncDef& def, bool anonymous_setter_enabled, bool named_setter_enabled, int closure_values, void * user_param)
{
const FuncDef list[] = {def, {}};
setFuncs(list, anonymous_setter_enabled, named_setter_enabled, closure_values, user_param);
}
void OS::setFuncs(const FuncDef * list, bool anonymous_setter_enabled, bool named_setter_enabled, int closure_values, void * user_param)
{
for(; list->func; list++){
pushStackValue(-1);
pushString(list->name);
// push closure_values for cfunction
for(int i = 0; i < closure_values; i++){
pushStackValue(-2-closure_values);
}
pushCFunction(list->func, closure_values, list->user_param ? list->user_param : user_param);
setProperty(anonymous_setter_enabled, named_setter_enabled);
}
}
void OS::setNumber(const NumberDef& def, bool anonymous_setter_enabled, bool named_setter_enabled)
{
const NumberDef list[] = {def, {}};
setNumbers(list, anonymous_setter_enabled, named_setter_enabled);
}
void OS::setNumbers(const NumberDef * list, bool anonymous_setter_enabled, bool named_setter_enabled)
{
for(; list->name; list++){
pushStackValue(-1);
pushString(list->name);
pushNumber(list->value);
setProperty(anonymous_setter_enabled, named_setter_enabled);
}
}
void OS::setString(const StringDef& def, bool anonymous_setter_enabled, bool named_setter_enabled)
{
const StringDef list[] = {def, {}};
setStrings(list, anonymous_setter_enabled, named_setter_enabled);
}
void OS::setStrings(const StringDef * list, bool anonymous_setter_enabled, bool named_setter_enabled)
{
for(; list->name; list++){
pushStackValue(-1);
pushString(list->name);
pushString(list->value);
setProperty(anonymous_setter_enabled, named_setter_enabled);
}
}
void OS::setNull(const NullDef& def, bool anonymous_setter_enabled, bool named_setter_enabled)
{
const NullDef list[] = {def, {}};
setNulls(list, anonymous_setter_enabled, named_setter_enabled);
}
void OS::setNulls(const NullDef * list, bool anonymous_setter_enabled, bool named_setter_enabled)
{
for(; list->name; list++){
pushStackValue(-1);
pushString(list->name);
pushNull();
setProperty(anonymous_setter_enabled, named_setter_enabled);
}
}
void OS::getObject(const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
pushStackValue(-1); // 2: copy parent object
pushString(name); // 3: index
getProperty(anonymous_getter_enabled, named_getter_enabled, prototype_enabled); // 2: value
if(isObject()){
remove(-2); // 1: remove parent object
return;
}
pop(); // 1: parent object
newObject(); // 2: result object
pushStackValue(-2); // 3: copy parent object
pushString(name); // 4: index
pushStackValue(-3); // 5: copy result object
setProperty(anonymous_getter_enabled, named_getter_enabled); // 2: parent + result
remove(-2); // 1: remove parent object
}
void OS::getGlobalObject(const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
pushGlobals();
getObject(name, anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::getModule(const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
getGlobalObject(name, anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
pushStackValue(-1);
pushGlobals();
setPrototype();
}
void OS::getGlobal(const OS_CHAR * name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
getGlobal(Core::String(this, name), anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::getGlobal(const Core::String& name, bool anonymous_getter_enabled, bool named_getter_enabled, bool prototype_enabled)
{
pushGlobals();
pushString(name);
getProperty(anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
}
void OS::setGlobal(const OS_CHAR * name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
setGlobal(Core::String(this, name), anonymous_setter_enabled, named_setter_enabled);
}
void OS::setGlobal(const Core::String& name, bool anonymous_setter_enabled, bool named_setter_enabled)
{
if(core->stack_values.count >= 1){
Core::Value object = core->global_vars;
Core::Value value = core->stack_values[core->stack_values.count - 1];
Core::Value index = core->pushStringValue(name);
core->setPropertyValue(object, Core::PropertyIndex(index), value, anonymous_setter_enabled, named_setter_enabled);
pop(2);
}
}
void OS::setGlobal(const FuncDef& func, bool anonymous_setter_enabled, bool named_setter_enabled)
{
pushCFunction(func.func, func.user_param);
setGlobal(func.name, anonymous_setter_enabled, named_setter_enabled);
}
void OS::initGlobalFunctions()
{
struct Lib
{
static int print(OS * os, int params, int, int, void*)
{
int params_offs = os->getAbsoluteOffs(-params);
for(int i = 0; i < params; i++){
String str = os->toString(params_offs + i);
os->printf("%s", str.toChar());
if(i+1 < params){
os->printf("\t");
}
}
if(params > 0){
os->printf("\n");
}
return 0;
}
static int echo(OS * os, int params, int, int, void*)
{
int params_offs = os->getAbsoluteOffs(-params);
for(int i = 0; i < params; i++){
String str = os->toString(params_offs + i);
os->printf("%s", str.toChar());
}
return 0;
}
static int concat(OS * os, int params, int, int, void*)
{
if(params < 1){
return 0;
}
OS::Core::StringBuffer buf(os);
int params_offs = os->getAbsoluteOffs(-params);
for(int i = 0; i < params; i++){
buf += os->toString(params_offs + i);
}
os->core->pushValue(buf.toGCStringValue());
return 1;
}
static int compileText(OS * os, int params, int, int need_ret_values, void*)
{
if(params < 1){
return 0;
}
os->compile();
return 1;
}
static int compileFile(OS * os, int params, int, int need_ret_values, void*)
{
if(params < 1){
return 0;
}
bool required = params > 1 ? os->toBool(-params+1) : false;
os->compileFile(os->toString(-params), required);
return 1;
}
static int resolvePath(OS * os, int params, int, int, void*)
{
String filename = os->resolvePath(os->toString(-1));
if(filename.getDataSize()){
os->pushString(filename);
return 1;
}
return 0;
}
static int debugBackTrace(OS * os, int params, int, int, void*)
{
switch(params){
case 0:
os->core->pushBackTrace(0, 10);
break;
case 1:
os->core->pushBackTrace(os->toInt(-params), 10);
break;
default:
os->core->pushBackTrace(os->toInt(-params), os->toInt(-params+1));
}
return 1;
}
static int terminate(OS * os, int params, int, int, void*)
{
os->setTerminated(true, os->toInt(-params));
return 0;
}
static int setErrorHandler(OS * os, int params, int, int, void*)
{
switch(params){
default:
return 0;
case 1:
os->setErrorHandler();
break;
case 2:
os->setErrorHandler(os->popInt());
break;
}
return 1;
}
static int triggerError(OS * os, int params, int, int, void*)
{
int code = os->toInt(-params, OS_E_ERROR);
String message = os->toString(-params+1, OS_TEXT("unknown error"));
os->triggerError(code, message);
return 0;
}
};
FuncDef list[] = {
{OS_TEXT("print"), Lib::print},
{OS_TEXT("echo"), Lib::echo},
{OS_TEXT("concat"), Lib::concat},
{OS_TEXT("compileText"), Lib::compileText},
{OS_TEXT("compileFile"), Lib::compileFile},
{OS_TEXT("resolvePath"), Lib::resolvePath},
{OS_TEXT("debugBackTrace"), Lib::debugBackTrace},
{OS_TEXT("terminate"), Lib::terminate},
{OS_TEXT("setErrorHandler"), Lib::setErrorHandler},
{OS_TEXT("triggerError"), Lib::triggerError},
{}
};
NumberDef numbers[] = {
{OS_TEXT("E_ERROR"), OS_E_ERROR},
{OS_TEXT("E_WARNING"), OS_E_WARNING},
{OS_TEXT("E_NOTICE"), OS_E_NOTICE},
{}
};
pushGlobals();
setFuncs(list);
setNumbers(numbers);
pop();
}
void OS::initObjectClass()
{
static intptr_t iterator_crc = (intptr_t)&iterator_crc;
static intptr_t array_iterator_crc = (intptr_t)&array_iterator_crc;
struct Object
{
static int rawget(OS * os, int params, int, int, void*)
{
bool anonymous_getter_enabled = false, named_getter_enabled = false, prototype_enabled = false;
switch(params){
case 0:
break;
default:
os->pop(params-4);
// no break
case 4:
prototype_enabled = os->popBool(false);
// no break
case 3:
named_getter_enabled = os->popBool(false);
// no break
case 2:
anonymous_getter_enabled = os->popBool(false);
// no break
case 1:
os->getProperty(anonymous_getter_enabled, named_getter_enabled, prototype_enabled);
return 1;
}
return 0;
}
static int rawset(OS * os, int params, int, int, void*)
{
bool anonymous_getter_enabled = false, named_getter_enabled = false;
switch(params){
case 0:
break;
default:
os->pop(params-4);
// no break
case 4:
named_getter_enabled = os->popBool(false);
// no break
case 3:
anonymous_getter_enabled = os->popBool(false);
// no break
case 2:
os->setProperty(anonymous_getter_enabled, named_getter_enabled);
}
return 0;
}
static int getValueId(OS * os, int params, int, int, void*)
{
os->pushNumber(os->getValueId(-params-1));
return 1;
}
static int iteratorStep(OS * os, int params, int closure_values, int, void*)
{
OS_ASSERT(closure_values == 2);
Core::Value self_var = os->core->getStackValue(-closure_values + 0);
void * p = os->toUserdata(iterator_crc, -closure_values + 1);
Core::Table::IteratorState * iter = (Core::Table::IteratorState*)p;
if(iter->table){
Core::GCValue * self = self_var.getGCValue();
OS_ASSERT(self && iter->table == self->table);
if(iter->prop){
os->pushBool(true);
os->core->pushValue(iter->prop->index);
os->core->pushValue(iter->prop->value);
iter->prop = iter->ascending ? iter->prop->next : iter->prop->prev;
return 3;
}
iter->table->removeIterator(iter);
}
return 0;
}
static void iteratorStateDestructor(OS * os, void * data, void * user_param)
{
Core::Table::IteratorState * iter = (Core::Table::IteratorState*)data;
if(iter->table){
iter->table->removeIterator(iter);
}
}
static int arrayIteratorStep(OS * os, int params, int closure_values, int, void*)
{
OS_ASSERT(closure_values == 2);
Core::Value self_var = os->core->getStackValue(-closure_values + 0);
int * pi = (int*)os->toUserdata(array_iterator_crc, -closure_values + 1);
OS_ASSERT(self_var.type == OS_VALUE_TYPE_ARRAY && pi && pi[1]);
if(pi[0] >= 0 && pi[0] < self_var.v.arr->values.count){
os->pushBool(true);
os->pushNumber(pi[0]);
os->core->pushValue(self_var.v.arr->values[pi[0]]);
pi[0] += pi[1];
return 3;
}
return 0;
}
static int iterator(OS * os, int params, bool ascending)
{
Core::Value self_var = os->core->getStackValue(-params-1);
if(self_var.type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
os->core->pushValue(self_var);
int * pi = (int*)os->pushUserdata(array_iterator_crc, sizeof(int)*2);
OS_ASSERT(pi);
pi[0] = ascending ? 0 : self_var.v.arr->values.count-1;
pi[1] = ascending ? 1 : -1;
os->pushCFunction(arrayIteratorStep, 2);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(self && self->table && self->table->count > 0){
typedef Core::Table::IteratorState IteratorState;
os->core->pushValue(self);
IteratorState * iter = (IteratorState*)os->pushUserdata(iterator_crc, sizeof(IteratorState), iteratorStateDestructor);
iter->table = NULL;
iter->next = NULL;
iter->prop = NULL;
iter->ascending = ascending;
self->table->addIterator(iter);
os->pushCFunction(iteratorStep, 2);
return 1;
}
return 0;
}
static int iterator(OS * os, int params, int closure_values, int, void*)
{
return iterator(os, params + closure_values, true);
}
static int reverseIterator(OS * os, int params, int closure_values, int, void*)
{
return iterator(os, params + closure_values, false);
}
static int smartSort(OS * os, int params,
int(*arrcomp)(OS*, const void*, const void*, void*),
int(*objcomp)(OS*, const void*, const void*, void*), void * user_param = NULL)
{
Core::Value self_var = os->core->getStackValue(-params-1);
if(self_var.type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
if(arrcomp){
os->core->sortArray(self_var.v.arr, arrcomp, user_param);
}
os->core->pushValue(self_var);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(self){
if(self->table && objcomp){
os->core->sortTable(self->table, objcomp, user_param);
}
os->core->pushValue(self_var);
return 1;
}
return 0;
}
static int sort(OS * os, int params, int, int, void*)
{
if(params < 1){
return smartSort(os, params, Core::compareArrayValues, Core::comparePropValues);
}
String prop_name(os);
if(os->core->isValueString(os->core->getStackValue(-params), &prop_name)){
return smartSort(os, params, NULL, Core::compareObjectProperties, &prop_name);
}
return smartSort(os, params, Core::compareUserArrayValues, Core::compareUserPropValues);
}
static int rsort(OS * os, int params, int, int, void*)
{
if(params < 1){
return smartSort(os, params, Core::compareArrayValuesReverse, Core::comparePropValuesReverse);
}
String prop_name(os);
if(os->core->isValueString(os->core->getStackValue(-params), &prop_name)){
return smartSort(os, params, NULL, Core::compareObjectPropertiesReverse, &prop_name);
}
return smartSort(os, params, Core::compareUserArrayValuesReverse, Core::compareUserPropValuesReverse);
}
static void userSortArrayByKeys(OS * os, Core::GCArrayValue * arr, int params, bool reverse)
{
Core::GCArrayValue * keys = os->core->pushArrayValue();
os->vectorReserveCapacity(keys->values, arr->values.count OS_DBG_FILEPOS);
keys->values.count = arr->values.count;
for(int i = 0; i < arr->values.count; i++){
keys->values[i] = i;
}
if(reverse){
os->core->sortArray(keys, Core::compareUserArrayValuesReverse);
}else{
os->core->sortArray(keys, Core::compareUserArrayValues);
}
Vector<Core::Value> values;
os->vectorReserveCapacity(values, arr->values.count OS_DBG_FILEPOS);
OS_MEMCPY(values.buf, arr->values.buf, sizeof(Core::Value) * arr->values.count);
values.count = arr->values.count;
for(int i = 0; i < arr->values.count; i++){
arr->values[i] = values[(int)os->core->valueToInt(keys->values[i])];
}
os->vectorClear(values);
os->vectorClear(keys->values);
os->pop();
}
static int ksort(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
if(self_var.type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
if(params > 0){
userSortArrayByKeys(os, self_var.v.arr, params, false);
}else{
// os->core->sortArray(self_var.v.arr, arrcomp);
// array is always sorted by keys so it's nothing to do
}
os->core->pushValue(self_var);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(self){
if(self->table){
if(params > 0){
os->core->sortTable(self->table, Core::compareUserPropKeys);
}else{
os->core->sortTable(self->table, Core::comparePropKeys);
}
}
os->core->pushValue(self_var);
return 1;
}
return 0;
}
static int krsort(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
if(self_var.type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
if(params > 0){
userSortArrayByKeys(os, self_var.v.arr, params, true);
}else{
int mid = self_var.v.arr->values.count/2;
for(int i = 0, j = self_var.v.arr->values.count-1; i < mid; i++, j--){
Core::Value tmp = self_var.v.arr->values[i];
self_var.v.arr->values[i] = self_var.v.arr->values[j];
self_var.v.arr->values[j] = tmp;
}
}
os->core->pushValue(self_var);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(self){
if(self->table){
if(params > 0){
os->core->sortTable(self->table, Core::compareUserPropKeysReverse);
}else{
os->core->sortTable(self->table, Core::comparePropKeysReverse);
}
}
os->core->pushValue(self_var);
return 1;
}
return 0;
}
static int length(OS * os, int params, int closure_values, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-closure_values-1);
if(self_var.type == OS_VALUE_TYPE_ARRAY){
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
os->pushNumber(self_var.v.arr->values.count);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(self){
os->pushNumber(self->table ? self->table->count : 0);
return 1;
}
return 0;
}
static void appendQuotedString(Core::StringBuffer& buf, const Core::String& string)
{
buf += OS_TEXT("\"");
int len = string.getLen();
const OS_CHAR * str = string.toChar();
for(int i = 0; i < len; i++, str++){
switch(*str){
case OS_TEXT('\"'): buf += OS_TEXT("\\\""); continue;
case OS_TEXT('\r'): buf += OS_TEXT("\\r"); continue;
case OS_TEXT('\n'): buf += OS_TEXT("\\n"); continue;
case OS_TEXT('\t'): buf += OS_TEXT("\\t"); continue;
case OS_TEXT('\\'): buf += OS_TEXT("\\\\"); continue;
}
if(*str < OS_TEXT(' ')){
buf += OS_TEXT("0x");
buf.append((OS_CHAR)'A' + ((int)*str >> 4));
buf.append((OS_CHAR)'A' + ((int)*str >> 0));
continue;
}
buf.append(*str);
}
buf += OS_TEXT("\"");
}
static int valueof(OS * os, int params, int closure_values, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-closure_values-1);
switch(self_var.type){
case OS_VALUE_TYPE_NULL:
os->pushString(os->core->strings->typeof_null);
return 1;
case OS_VALUE_TYPE_BOOL:
os->pushString(self_var.v.boolean ? os->core->strings->syntax_true : os->core->strings->syntax_false);
return 1;
case OS_VALUE_TYPE_NUMBER:
case OS_VALUE_TYPE_STRING:
os->core->pushValue(self_var);
return 1;
}
Core::GCValue * self = self_var.getGCValue();
if(!self){
return 0;
}
switch(self->type){
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
Core::StringBuffer str(os);
str += OS_TEXT("<");
str += os->core->strings->typeof_userdata;
str += OS_TEXT(":");
str += Core::String(os, (OS_INT)self->value_id);
str += OS_TEXT(">");
os->pushString(str);
return 1;
}
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
{
Core::StringBuffer str(os);
str += OS_TEXT("<");
str += os->core->strings->typeof_function;
str += OS_TEXT(":");
str += Core::String(os, (OS_INT)self->value_id);
str += OS_TEXT(">");
os->pushString(str);
return 1;
}
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self));
Core::GCArrayValue * arr = (Core::GCArrayValue*)self;
Core::StringBuffer buf(os);
buf += OS_TEXT("[");
Core::Value temp;
for(int i = 0; i < arr->values.count; i++){
if(i > 0){
buf += OS_TEXT(",");
}
Core::Value value = arr->values[i];
if(os->core->getPropertyValue(temp, os->core->check_recursion, value, false)){
buf += OS_TEXT("<<RECURSION>>");
continue;
}
Core::GCValue * gcvalue = value.getGCValue();
if(gcvalue && gcvalue->table && gcvalue->table->count){
os->core->setPropertyValue(os->core->check_recursion, value, Core::Value(true), false, false);
}
Core::String value_str = os->core->valueToString(value, true);
if(value.type == OS_VALUE_TYPE_STRING){
appendQuotedString(buf, value_str);
}else{
buf += value_str;
}
}
buf += OS_TEXT("]");
os->pushString(buf);
return 1;
}
case OS_VALUE_TYPE_OBJECT:
if(!self->table || !self->table->count){
os->pushString(OS_TEXT("{}"));
return 1;
}
{
Core::StringBuffer buf(os);
buf += OS_TEXT("{");
int need_index = 0;
Core::Property * prop = self->table->first;
Core::Value temp;
for(int i = 0; prop; prop = prop->next, i++){
if(i > 0){
buf += OS_TEXT(",");
}
if(prop->index.type == OS_VALUE_TYPE_NUMBER){
if(prop->index.v.number != (OS_FLOAT)need_index){
buf += String(os, prop->index.v.number, OS_AUTO_PRECISION);
buf += OS_TEXT(":");
}
need_index = (int)(prop->index.v.number + 1);
}else if(prop->index.type == OS_VALUE_TYPE_STRING){
OS_ASSERT(!prop->index.v.string->table);
appendQuotedString(buf, os->core->valueToString(prop->index));
buf += OS_TEXT(":");
}else{
Core::GCValue * gcvalue = prop->index.getGCValue();
if(os->core->getPropertyValue(temp, os->core->check_recursion, prop->index, false)){
buf += OS_TEXT("<<RECURSION>>");
}else{
if(gcvalue && gcvalue->table && gcvalue->table->count){
os->core->setPropertyValue(os->core->check_recursion, prop->index, Core::Value(true), false, false);
}
buf += os->core->valueToString(prop->index, true);
}
buf += OS_TEXT(":");
}
if(os->core->getPropertyValue(temp, os->core->check_recursion, prop->value, false)){
buf += OS_TEXT("<<RECURSION>>");
continue;
}
Core::GCValue * gcvalue = prop->value.getGCValue();
if(gcvalue && gcvalue->table && gcvalue->table->count){
os->core->setPropertyValue(os->core->check_recursion, prop->value, Core::Value(true), false, false);
}
Core::String value_str = os->core->valueToString(prop->value, true);
if(prop->value.type == OS_VALUE_TYPE_STRING){
appendQuotedString(buf, value_str);
}else{
buf += value_str;
}
}
os->pushString(buf += OS_TEXT("}"));
return 1;
}
}
return 0;
}
static int push(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
Core::Value value = os->core->getStackValue(-params);
OS_INT num_index = 0;
switch(self_var.type){
case OS_VALUE_TYPE_ARRAY:
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
os->vectorAddItem(self_var.v.arr->values, value OS_DBG_FILEPOS);
// os->pushNumber(self_var.v.arr->values.count);
os->core->pushValue(value);
return 1;
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
num_index = self_var.v.object->table ? self_var.v.object->table->next_index : 0;
break;
default:
return 0;
}
os->core->setPropertyValue(self_var, Core::PropertyIndex(num_index), value, false, false);
// os->pushNumber(self_var.v.object->table->count);
os->core->pushValue(value);
return 1;
}
static int pop(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
switch(self_var.type){
case OS_VALUE_TYPE_ARRAY:
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
if(self_var.v.arr->values.count > 0){
os->core->pushValue(self_var.v.arr->values.lastElement());
os->vectorRemoveAtIndex(self_var.v.arr->values, self_var.v.arr->values.count-1);
return 1;
}
return 0;
case OS_VALUE_TYPE_OBJECT:
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
case OS_VALUE_TYPE_FUNCTION:
case OS_VALUE_TYPE_CFUNCTION:
if(self_var.v.object->table && self_var.v.object->table->count > 0){
os->core->pushValue(self_var.v.object->table->last->value);
Core::PropertyIndex index = *self_var.v.object->table->last;
os->core->deleteValueProperty(self_var.v.object, index, false, false, false);
return 1;
}
break;
}
return 0;
}
static int hasOwnProperty(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
Core::Value index = os->core->getStackValue(-params);
Core::GCValue * self = self_var.getGCValue();
if(self){
os->pushBool( os->core->hasProperty(self, index, true, true, false) );
return 1;
}
return 0;
}
static int hasProperty(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
Core::Value index = os->core->getStackValue(-params);
Core::GCValue * self = self_var.getGCValue();
if(self){
os->pushBool( os->core->hasProperty(self, index, true, true, true) );
return 1;
}
return 0;
}
static int sub(OS * os, int params, int, int, void*)
{
int start, len, size;
Core::Value self_var = os->core->getStackValue(-params-1);
switch(self_var.type){
case OS_VALUE_TYPE_OBJECT:
OS_ASSERT(dynamic_cast<Core::GCObjectValue*>(self_var.v.object));
size = self_var.v.object->table ? self_var.v.object->table->count : 0;
break;
default:
return 0;
}
switch(params){
case 0:
os->core->pushValue(self_var);
return 1;
case 1:
start = os->toInt(-params);
len = size;
break;
default:
start = os->toInt(-params);
len = os->toInt(-params+1);
}
if(start < 0){
start = size + start;
if(start < 0){
start = 0;
}
}
if(start >= size){
os->newObject();
return 1;
}
if(len < 0){
len = size - start + len;
}
if(len <= 0){
os->newObject();
return 1;
}
if(start + len > size){
len = size - start;
}
if(!start && len == size){
os->core->pushValue(self_var);
return 1;
}
OS_ASSERT(self_var.v.object->table && self_var.v.object->table->first);
Core::GCObjectValue * object = os->core->pushObjectValue(self_var.v.object->prototype);
Core::Property * prop = self_var.v.object->table->first;
int i = 0;
for(; i < start; i++){
prop = prop->next;
OS_ASSERT(prop);
}
Vector<Core::Value> captured_items;
os->vectorReserveCapacity(captured_items, len*2 OS_DBG_FILEPOS);
for(i = 0; i < len; i++, prop = prop->next){
OS_ASSERT(prop);
os->vectorAddItem(captured_items, prop->index OS_DBG_FILEPOS);
os->vectorAddItem(captured_items, prop->value OS_DBG_FILEPOS);
}
for(i = 0; i < len; i++){
os->core->setPropertyValue(object, captured_items[i*2], captured_items[i*2+1], false, false);
}
os->vectorClear(captured_items);
return 1;
}
static int merge(OS * os, int params, int, int, void*)
{
if(params < 1) return 0;
int offs = os->getAbsoluteOffs(-params);
bool is_array = os->isArray(offs-1);
if(is_array || os->isObject(offs-1)){
for(int i = 0; i < params; i++){
Core::Value value = os->core->getStackValue(offs+i);
switch(value.type){
case OS_VALUE_TYPE_ARRAY:
{
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(value.v.arr));
for(int j = 0; j < value.v.arr->values.count; j++){
os->pushStackValue(offs-1);
os->core->pushValue(value.v.arr->values[j]);
os->addProperty();
}
break;
}
case OS_VALUE_TYPE_OBJECT:
{
OS_ASSERT(dynamic_cast<Core::GCObjectValue*>(value.v.object));
if(value.v.object->table){
Core::Property * prop = value.v.object->table->first;
for(; prop; prop = prop->next){
os->pushStackValue(offs-1);
if(is_array){
os->core->pushValue(prop->value);
os->addProperty();
}else{
os->core->pushValue(prop->index);
os->core->pushValue(prop->value);
os->setProperty();
}
}
}
break;
}
}
}
os->pushStackValue(offs-1);
return 1;
}
return 0;
}
static int getKeys(OS * os, int params, int, int, void*)
{
Core::Value value = os->core->getStackValue(-params-1);
switch(value.type){
case OS_VALUE_TYPE_ARRAY:
{
Core::GCArrayValue * arr = os->core->pushArrayValue(value.v.arr->values.count);
for(int i = 0; i < value.v.arr->values.count; i++){
os->vectorAddItem(arr->values, Core::Value(i) OS_DBG_FILEPOS);
}
return 1;
}
case OS_VALUE_TYPE_OBJECT:
{
if(value.v.object->table){
Core::GCArrayValue * arr = os->core->pushArrayValue(value.v.object->table->count);
Core::Property * prop = value.v.object->table->first;
for(int i = 0; prop; prop = prop->next, i++){
os->vectorAddItem(arr->values, prop->index OS_DBG_FILEPOS);
}
}else{
os->newArray();
}
return 1;
}
}
return 0;
}
static int getValues(OS * os, int params, int, int, void*)
{
Core::Value value = os->core->getStackValue(-params-1);
switch(value.type){
case OS_VALUE_TYPE_ARRAY:
os->core->pushValue(value);
return 1;
case OS_VALUE_TYPE_OBJECT:
{
if(value.v.object->table){
Core::GCArrayValue * arr = os->core->pushArrayValue(value.v.object->table->count);
Core::Property * prop = value.v.object->table->first;
for(int i = 0; prop; prop = prop->next, i++){
os->vectorAddItem(arr->values, prop->value OS_DBG_FILEPOS);
}
}else{
os->newArray();
}
return 1;
}
}
return 0;
}
};
FuncDef list[] = {
{OS_TEXT("rawget"), Object::rawget},
{OS_TEXT("rawset"), Object::rawset},
{OS_TEXT("__get@osValueId"), Object::getValueId},
{core->strings->__len, Object::length},
// {OS_TEXT("__get@length"), Object::length},
{core->strings->__iter, Object::iterator},
{OS_TEXT("reverseIter"), Object::reverseIterator},
{core->strings->__valueof, Object::valueof},
{OS_TEXT("sort"), Object::sort},
{OS_TEXT("rsort"), Object::rsort},
{OS_TEXT("ksort"), Object::ksort},
{OS_TEXT("krsort"), Object::krsort},
{OS_TEXT("push"), Object::push},
{OS_TEXT("pop"), Object::pop},
{OS_TEXT("hasOwnProperty"), Object::hasOwnProperty},
{OS_TEXT("hasProperty"), Object::hasProperty},
{OS_TEXT("merge"), Object::merge},
{OS_TEXT("getKeys"), Object::getKeys},
{OS_TEXT("getValues"), Object::getValues},
{OS_TEXT("__get@keys"), Object::getKeys},
{OS_TEXT("__get@values"), Object::getValues},
{}
};
core->pushValue(core->prototypes[Core::PROTOTYPE_OBJECT]);
setFuncs(list);
pop();
}
void OS::initArrayClass()
{
struct Array
{
static int sub(OS * os, int params, int, int, void*)
{
int start, len, size;
Core::Value self_var = os->core->getStackValue(-params-1);
switch(self_var.type){
case OS_VALUE_TYPE_ARRAY:
OS_ASSERT(dynamic_cast<Core::GCArrayValue*>(self_var.v.arr));
size = self_var.v.arr->values.count;
break;
default:
return 0;
}
switch(params){
case 0:
os->core->pushValue(self_var);
return 1;
case 1:
start = os->toInt(-params);
len = size;
break;
default:
start = os->toInt(-params);
len = os->toInt(-params+1);
}
if(start < 0){
start = size + start;
if(start < 0){
start = 0;
}
}
if(start >= size){
os->newArray();
return 1;
}
if(len < 0){
len = size - start + len;
}
if(len <= 0){
os->newArray();
return 1;
}
if(start + len > size){
len = size - start;
}
if(!start && len == size){
os->core->pushValue(self_var);
return 1;
}
Core::GCArrayValue * arr = os->core->pushArrayValue(len);
for(int i = 0; i < len; i++){
os->vectorAddItem(arr->values, self_var.v.arr->values[start+i] OS_DBG_FILEPOS);
}
return 1;
}
};
FuncDef list[] = {
{OS_TEXT("sub"), Array::sub},
{}
};
core->pushValue(core->prototypes[Core::PROTOTYPE_ARRAY]);
setFuncs(list);
pop();
}
void OS::initStringClass()
{
struct String
{
static int length(OS * os, int params, int, int, void*)
{
Core::Value self_var = os->core->getStackValue(-params-1);
Core::GCValue * self = self_var.getGCValue();
if(self){
if(self->type == OS_VALUE_TYPE_STRING){
Core::GCStringValue * string = (Core::GCStringValue*)self;
os->pushNumber(string->getLen());
// os->pushNumber(os->core->valueToString(self).getDataSize() / sizeof(OS_CHAR));
return 1;
}
os->core->pushOpResultValue(Core::Program::OP_LENGTH, self_var);
return 1;
}
return 0;
}
static int sub(OS * os, int params, int, int, void*)
{
int start, len;
OS::String str = os->toString(-params-1);
int size = str.getLen();
switch(params){
case 0:
os->pushStackValue(-params-1);
return 1;
case 1:
start = os->toInt(-params);
len = size;
break;
default:
start = os->toInt(-params);
len = os->toInt(-params+1);
}
if(start < 0){
start = size + start;
if(start < 0){
start = 0;
}
}
if(start >= size){
os->pushString(OS_TEXT(""));
return 1;
}
if(len < 0){
len = size - start + len;
}
if(len <= 0){
os->pushString(OS_TEXT(""));
return 1;
}
if(start + len > size){
len = size - start;
}
if(!start && len == size){
os->pushStackValue(-params-1);
return 1;
}
os->pushString(str.toChar() + start, len);
return 1;
}
};
FuncDef list[] = {
{core->strings->__len, String::length},
{OS_TEXT("sub"), String::sub},
// {OS_TEXT("__get@length"), String::length},
{}
};
core->pushValue(core->prototypes[Core::PROTOTYPE_STRING]);
setFuncs(list);
pop();
}
void OS::initFunctionClass()
{
struct Function
{
static int apply(OS * os, int params, int, int need_ret_values, void*)
{
int offs = os->getAbsoluteOffs(-params);
os->pushStackValue(offs-1); // self as func
if(params < 1){
os->pushNull();
return os->call(0, need_ret_values);
}
os->pushStackValue(offs); // first param - new this
Core::Value array_var = os->core->getStackValue(offs+1);
if(array_var.type == OS_VALUE_TYPE_ARRAY){
int count = array_var.v.arr->values.count;
for(int i = 0; i < count; i++){
os->core->pushValue(array_var.v.arr->values[i]);
}
return os->call(count, need_ret_values);
}
return os->call(0, need_ret_values);
}
static int call(OS * os, int params, int, int need_ret_values, void*)
{
#if 1 // speed optimization
return os->call(params-1, need_ret_values);
#else
int offs = os->getAbsoluteOffs(-params);
os->pushStackValue(offs-1); // self as func
if(params < 1){
os->pushNull(); // this
return os->call(0, need_ret_values);
}
os->pushStackValue(offs); // first param - new this
for(int i = 1; i < params; i++){
os->pushStackValue(offs + i);
}
return os->call(params-1, need_ret_values);
#endif
}
static int applyEnv(OS * os, int params, int, int need_ret_values, void *)
{
Core::Value save_env;
Core::Value func = os->core->getStackValue(-params-1);
if(func.type == OS_VALUE_TYPE_FUNCTION){
save_env = func.v.func->env;
func.v.func->env = os->core->getStackValue(-params).getGCValue();
}
os->remove(-params);
int r = apply(os, params-1, 0, need_ret_values, NULL);
if(func.type == OS_VALUE_TYPE_FUNCTION){
func.v.func->env = save_env;
}
return r;
}
static int callEnv(OS * os, int params, int, int need_ret_values, void *)
{
Core::Value save_env;
Core::Value func = os->core->getStackValue(-params-1);
if(func.type == OS_VALUE_TYPE_FUNCTION){
save_env = func.v.func->env;
func.v.func->env = os->core->getStackValue(-params).getGCValue();
}
os->remove(-params);
int r = call(os, params-1, 0, need_ret_values, NULL);
if(func.type == OS_VALUE_TYPE_FUNCTION){
func.v.func->env = save_env;
}
return r;
}
static int getEnv(OS * os, int params, int, int, void*)
{
Core::Value func = os->core->getStackValue(-params-1);
if(func.type == OS_VALUE_TYPE_FUNCTION){
os->core->pushValue(func.v.func->env);
return 1;
}
return 0;
}
static int setEnv(OS * os, int params, int, int, void*)
{
Core::Value func = os->core->getStackValue(-params-1);
if(func.type == OS_VALUE_TYPE_FUNCTION){
Core::Value env = os->core->getStackValue(-params);
func.v.func->env = env.getGCValue();
}
return 0;
}
static int iterator(OS * os, int params, int, int need_ret_values, void*)
{
os->pushStackValue(-params-1); // self as func
return 1;
}
};
FuncDef list[] = {
{OS_TEXT("apply"), Function::apply},
{OS_TEXT("applyEnv"), Function::applyEnv},
{OS_TEXT("call"), Function::call},
{OS_TEXT("callEnv"), Function::callEnv},
{OS_TEXT("__get@") OS_ENV_VAR_NAME, Function::getEnv},
{OS_TEXT("__set@") OS_ENV_VAR_NAME, Function::setEnv},
{core->strings->__iter, Function::iterator},
{}
};
core->pushValue(core->prototypes[Core::PROTOTYPE_FUNCTION]);
setFuncs(list);
pop();
}
/*
The following functions are based on a C++ class MTRand by
Richard J. Wagner. For more information see the web page at
http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
It's port from PHP framework.
*/
#define OS_RAND_N RAND_STATE_SIZE /* length of state vector */
#define OS_RAND_M (397) /* a period parameter */
#define OS_RAND_hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
#define OS_RAND_loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
#define OS_RAND_loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
#define OS_RAND_mixBits(u, v) (OS_RAND_hiBit(u)|OS_RAND_loBits(v)) /* move hi bit of u to hi bit of v */
#define OS_RAND_twist(m,u,v) (m ^ (OS_RAND_mixBits(u,v)>>1) ^ ((OS_U32)(-(OS_INT32)(OS_RAND_loBit(u))) & 0x9908b0dfU))
#define OS_RAND_MAX 0x7FFFFFFF /* (1<<31) - 1 */
#define OS_RAND_RANGE(__n, __min, __max, __tmax) \
(__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
#if defined _MSC_VER && !defined IW_SDK
#include <windows.h>
#define OS_RAND_GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ ((long) (1000000.0)))
// #elif !defined IW_SDK
// #define OS_RAND_GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0)))
#else
#define OS_RAND_GENERATE_SEED() (((long) (time(0))) ^ ((long) (1000000.0)))
#endif
void OS::Core::randInitialize(OS_U32 seed)
{
rand_seed = seed;
OS_U32 * s = rand_state;
OS_U32 * r = s;
*s++ = seed & 0xffffffffU;
for(int i = 1; i < OS_RAND_N; ++i ) {
*s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
r++;
}
randReload();
}
void OS::Core::randReload()
{
/* Generate N new values in state
Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
OS_U32 * state = rand_state;
OS_U32 * p = state;
int i;
for(i = OS_RAND_N - OS_RAND_M; i--; ++p){
*p = OS_RAND_twist(p[OS_RAND_M], p[0], p[1]);
}
for(i = OS_RAND_M; --i; ++p){
*p = OS_RAND_twist(p[OS_RAND_M-OS_RAND_N], p[0], p[1]);
}
*p = OS_RAND_twist(p[OS_RAND_M-OS_RAND_N], p[0], state[0]);
rand_left = OS_RAND_N;
rand_next = state;
}
double OS::Core::getRand()
{
/* Pull a 32-bit integer from the generator state
Every other access function simply transforms the numbers extracted here */
if(!rand_left){
if(!rand_next){
randInitialize(OS_RAND_GENERATE_SEED());
}else{
randReload();
}
}
--rand_left;
OS_U32 s1 = *rand_next++;
s1 ^= (s1 >> 11);
s1 ^= (s1 << 7) & 0x9d2c5680U;
s1 ^= (s1 << 15) & 0xefc60000U;
return (double)((s1 ^ (s1 >> 18))>>1) / (double)OS_RAND_MAX;
}
double OS::Core::getRand(double up)
{
return ::floor(getRand()*(up-1) + 0.5f);
}
double OS::Core::getRand(double min, double max)
{
return getRand() * (max - min) + min;
}
#define OS_MATH_PI ((OS_NUMBER)3.1415926535897932384626433832795)
#define OS_RADIANS_PER_DEGREE (OS_MATH_PI/180.0f)
void OS::initMathModule()
{
struct Math
{
static int minmax(OS * os, int params, OS_EOpcode opcode)
{
OS_ASSERT(params >= 0);
if(params <= 1){
return params;
}
int params_offs = os->getAbsoluteOffs(-params);
os->pushStackValue(params_offs); // save temp result
for(int i = 1; i < params; i++){
os->pushStackValue(-1); // copy temp result
os->pushStackValue(params_offs + i);
os->runOp(opcode); // remove params & push op result
if(!os->toBool()){
os->pop(2); // remove op result and temp result
os->pushStackValue(params_offs + i); // save temp result
continue;
}
os->pop();
}
return 1;
}
static int min_func(OS * os, int params, int, int, void*)
{
return minmax(os, params, OP_LOGIC_LE);
}
static int max_func(OS * os, int params, int, int, void*)
{
return minmax(os, params, OP_LOGIC_GE);
}
static double abs(double p)
{
return ::fabs(p);
}
static double ceil(double p)
{
return ::ceil(p);
}
static double floor(double p)
{
return ::floor(p);
}
static double round(double a, int precision)
{
if(precision <= 0){
if(precision < 0){
double p = 10.0f;
for(int i = -precision-1; i > 0; i--){
p *= 10.0f;
}
return ::floor(a / p + 0.5f) * p;
}
return ::floor(a + 0.5f);
}
double p = 10.0f;
for(int i = precision-1; i > 0; i--){
p *= 10.0f;
}
return ::floor(a * p + 0.5f) / p;
}
static double sin(double p)
{
return ::sin(p);
}
static double sinh(double p)
{
return ::sinh(p);
}
static double cos(double p)
{
return ::cos(p);
}
static double cosh(double p)
{
return ::cosh(p);
}
static double tan(double p)
{
return ::tan(p);
}
static double tanh(double p)
{
return ::tanh(p);
}
static double acos(double p)
{
return ::acos(p);
}
static double asin(double p)
{
return ::asin(p);
}
static double atan(double p)
{
return ::atan(p);
}
static double atan2(double y, double x)
{
return ::atan2(y, x);
}
static double exp(double p)
{
return ::exp(p);
}
static int frexp(OS * os, int params, int, int, void*)
{
if(!params) return 0;
int e;
os->pushNumber(::frexp(os->toNumber(-params), &e));
os->pushNumber(e);
return 2;
}
static double ldexp(double x, int y)
{
return ::ldexp(x, y);
}
static double pow(double x, double y)
{
return ::pow(x, y);
}
static int random(OS * os, int params, int, int, void*)
{
OS::Core * core = os->core;
switch(params){
case 0:
os->pushNumber(core->getRand());
return 1;
case 1:
os->pushNumber(core->getRand(os->toNumber(-params)));
return 1;
case 2:
default:
os->pushNumber(core->getRand(os->toNumber(-params), os->toNumber(-params+1)));
return 1;
}
return 0;
}
static int getrandseed(OS * os, int params, int, int, void*)
{
os->pushNumber((OS_NUMBER)os->core->rand_seed);
return 1;
}
static int setrandseed(OS * os, int params, int, int, void*)
{
if(!params) return 0;
os->core->rand_seed = (OS_U32)os->toNumber(-params);
return 0;
}
static double fmod(double x, double y)
{
return ::fmod(x, y);
}
static int modf(OS * os, int params, int, int, void*)
{
if(!params) return 0;
double ip;
double fp = ::modf(os->toNumber(-params), &ip);
os->pushNumber(ip);
os->pushNumber(fp);
return 2;
}
static double sqrt(double p)
{
return ::sqrt(p);
}
static int log(OS * os, int params, int, int, void*)
{
if(!params) return 0;
double x = os->toNumber(-params);
OS_NUMBER base;
if(params > 1 && os->isNumber(-params+1, &base)){
if(base == 10){
os->pushNumber(::log10(x));
}else{
os->pushNumber(::log(x)/::log(base));
}
}else{
os->pushNumber(::log(x));
}
return 1;
}
static double deg(double p)
{
return p / OS_RADIANS_PER_DEGREE;
}
static double rad(double p)
{
return p * OS_RADIANS_PER_DEGREE;
}
};
FuncDef list[] = {
{OS_TEXT("min"), Math::min_func},
{OS_TEXT("max"), Math::max_func},
def(OS_TEXT("abs"), Math::abs),
def(OS_TEXT("ceil"), Math::ceil),
def(OS_TEXT("floor"), Math::floor),
def(OS_TEXT("round"), Math::round),
def(OS_TEXT("sin"), Math::sin),
def(OS_TEXT("sinh"), Math::sinh),
def(OS_TEXT("cos"), Math::cos),
def(OS_TEXT("cosh"), Math::cosh),
def(OS_TEXT("tan"), Math::tan),
def(OS_TEXT("tanh"), Math::tanh),
def(OS_TEXT("acos"), Math::acos),
def(OS_TEXT("asin"), Math::asin),
def(OS_TEXT("atan"), Math::atan),
def(OS_TEXT("atan2"), Math::atan2),
def(OS_TEXT("exp"), Math::exp),
{OS_TEXT("frexp"), Math::frexp},
def(OS_TEXT("ldexp"), Math::ldexp),
def(OS_TEXT("pow"), Math::pow),
{OS_TEXT("random"), Math::random},
{OS_TEXT("__get@randseed"), Math::getrandseed},
{OS_TEXT("__set@randseed"), Math::setrandseed},
def(OS_TEXT("fmod"), Math::fmod),
{OS_TEXT("modf"), Math::modf},
def(OS_TEXT("sqrt"), Math::sqrt),
{OS_TEXT("log"), Math::log},
def(OS_TEXT("deg"), Math::deg),
def(OS_TEXT("rad"), Math::rad),
{}
};
NumberDef numbers[] = {
{OS_TEXT("PI"), OS_MATH_PI},
{OS_TEXT("MAX_NUMBER"), OS_MAX_NUMBER},
{}
};
getModule(OS_TEXT("math"));
setFuncs(list);
setNumbers(numbers);
pop();
}
void OS::initGCModule()
{
struct GC
{
static int getAllocatedBytes(OS * os, int params, int, int, void*)
{
os->pushNumber(os->getAllocatedBytes());
return 1;
}
static int getMaxAllocatedBytes(OS * os, int params, int, int, void*)
{
os->pushNumber(os->getMaxAllocatedBytes());
return 1;
}
static int getCachedBytes(OS * os, int params, int, int, void*)
{
os->pushNumber(os->getCachedBytes());
return 1;
}
static int getNumObjects(OS * os, int params, int, int, void*)
{
os->pushNumber(os->core->values.count);
return 1;
}
static int getNumCreatedObjects(OS * os, int params, int, int, void*)
{
os->pushNumber(os->core->num_created_values);
return 1;
}
static int getNumDestroyedObjects(OS * os, int params, int, int, void*)
{
os->pushNumber(os->core->num_destroyed_values);
return 1;
}
};
FuncDef list[] = {
{OS_TEXT("__get@allocatedBytes"), GC::getAllocatedBytes},
{OS_TEXT("__get@maxAllocatedBytes"), GC::getMaxAllocatedBytes},
{OS_TEXT("__get@cachedBytes"), GC::getCachedBytes},
{OS_TEXT("__get@numObjects"), GC::getNumObjects},
{OS_TEXT("__get@numCreatedObjects"), GC::getNumCreatedObjects},
{OS_TEXT("__get@numDestroyedObjects"), GC::getNumDestroyedObjects},
{}
};
getModule(OS_TEXT("GC"));
setFuncs(list);
pop();
}
void OS::initLangTokenizerModule()
{
struct LangTokenizer
{
enum {
TOKEN_TYPE_STRING,
TOKEN_TYPE_NUMBER,
TOKEN_TYPE_NAME,
TOKEN_TYPE_OPERATOR
};
static int getTokenType(Core::TokenType type)
{
switch(type){
case Core::Tokenizer::NAME:
return TOKEN_TYPE_NAME;
case Core::Tokenizer::STRING:
return TOKEN_TYPE_STRING;
case Core::Tokenizer::NUMBER:
return TOKEN_TYPE_NUMBER;
}
return TOKEN_TYPE_OPERATOR;
}
static void pushTokensAsObject(OS * os, Core::Tokenizer& tokenizer)
{
os->newArray();
int count = tokenizer.getNumTokens();
for(int i = 0; i < count; i++){
os->pushStackValue(-1);
os->newObject();
{
Core::TokenData * token = tokenizer.getToken(i);
os->pushStackValue(-1);
os->pushString(OS_TEXT("str"));
os->pushString(token->str);
os->setProperty();
#if 0
os->pushStackValue(-1);
os->pushString(OS_TEXT("line"));
os->pushNumber(token->line+1);
os->setProperty();
os->pushStackValue(-1);
os->pushString(OS_TEXT("pos"));
os->pushNumber(token->pos+1);
os->setProperty();
#endif
os->pushStackValue(-1);
os->pushString(OS_TEXT("type"));
os->pushNumber(getTokenType(token->type));
os->setProperty();
}
os->addProperty();
}
}
static int parseText(OS * os, int params, int, int, void*)
{
String str = os->toString(-params);
if(str.getDataSize() == 0){
return 0;
}
Core::Tokenizer tokenizer(os);
tokenizer.parseText(str.toChar(), str.getLen(), String(os));
pushTokensAsObject(os, tokenizer);
return 1;
}
static int parseFile(OS * os, int params, int, int, void*)
{
String filename = os->resolvePath(os->toString(-params));
if(filename.getDataSize() == 0){
return 0;
}
Core::FileStreamReader file(os, filename);
if(!file.f){
return 0;
}
Core::MemStreamWriter file_data(os);
file_data.writeFromStream(&file);
Core::Tokenizer tokenizer(os);
tokenizer.parseText((OS_CHAR*)file_data.buffer.buf, file_data.buffer.count, filename);
pushTokensAsObject(os, tokenizer);
return 1;
}
};
FuncDef list[] = {
{OS_TEXT("parseText"), LangTokenizer::parseText},
{OS_TEXT("parseFile"), LangTokenizer::parseFile},
{}
};
NumberDef numbers[] = {
{OS_TEXT("TOKEN_TYPE_STRING"), LangTokenizer::TOKEN_TYPE_STRING},
{OS_TEXT("TOKEN_TYPE_NUMBER"), LangTokenizer::TOKEN_TYPE_NUMBER},
{OS_TEXT("TOKEN_TYPE_NAME"), LangTokenizer::TOKEN_TYPE_NAME},
{OS_TEXT("TOKEN_TYPE_OPERATOR"), LangTokenizer::TOKEN_TYPE_OPERATOR},
{}
};
getModule(OS_TEXT("LangTokenizer"));
setFuncs(list);
setNumbers(numbers);
pop();
}
#define OS_AUTO_TEXT(exp) OS_TEXT(#exp)
void OS::initPreScript()
{
eval(OS_AUTO_TEXT(
// it's ObjectScript code here
Object.__get@length = function(){ return #this }
modules_loaded = {}
function require(filename, required){
filename = resolvePath(filename)
return filename && (modules_loaded.rawget(filename)
|| function(){
modules_loaded[filename] = {} // block recursive require
modules_loaded[filename] = compileFile(filename, required)()
return modules_loaded[filename]
}())
}
));
}
void OS::initPostScript()
{
eval(OS_AUTO_TEXT(
// it's ObjectScript code here
Object.__setempty = Object.push
Object.__getempty = Object.pop
));
}
int OS::Core::syncRetValues(int need_ret_values, int cur_ret_values)
{
if(cur_ret_values > need_ret_values){
pop(cur_ret_values - need_ret_values);
}else{
for(; cur_ret_values < need_ret_values; cur_ret_values++){
pushNull();
}
}
return need_ret_values;
}
OS::Core::GCObjectValue * OS::Core::initObjectInstance(GCObjectValue * object)
{
struct Lib {
static GCObjectValue * initObjectInstance_r(Core * core, GCObjectValue * object, GCValue * prototype)
{
if(prototype->prototype){
initObjectInstance_r(core, object, prototype->prototype);
}
Value value;
if(core->getPropertyValue(value, prototype, PropertyIndex(core->strings->__object, PropertyIndex::KeepStringIndex()), false)){
GCValue * object_props = value.getGCValue();
if(object_props && object_props->table){
Property * prop = object_props->table->first;
for(; prop; prop = prop->next){
core->pushCloneValue(prop->value);
core->setPropertyValue(object, *prop, core->stack_values.lastElement(), true, true);
core->pop();
}
}
}
return object;
}
};
if(object->prototype){
Lib::initObjectInstance_r(this, object, object->prototype);
}
return object;
}
void OS::Core::pushArguments(StackFunction * stack_func)
{
if(!stack_func->arguments){
int i;
GCArrayValue * args = pushArrayValue();
Upvalues * func_upvalues = stack_func->locals;
int num_params = stack_func->num_params - stack_func->num_extra_params;
for(i = 0; i < num_params; i++){
allocator->vectorAddItem(args->values, func_upvalues->locals[i] OS_DBG_FILEPOS);
}
int num_locals = func_upvalues->num_locals;
for(i = 0; i < stack_func->num_extra_params; i++){
allocator->vectorAddItem(args->values, func_upvalues->locals[i + num_locals] OS_DBG_FILEPOS);
}
stack_func->arguments = args;
}else{
pushValue(stack_func->arguments);
}
}
void OS::Core::pushArgumentsWithNames(StackFunction * stack_func)
{
int i;
GCObjectValue * args = pushObjectValue();
Upvalues * func_upvalues = stack_func->locals;
FunctionDecl * func_decl = stack_func->func->func_decl;
int num_params = stack_func->num_params - stack_func->num_extra_params;
for(i = 0; i < num_params; i++){
setPropertyValue(args, PropertyIndex(func_decl->locals[i].name.string, PropertyIndex::KeepStringIndex()), func_upvalues->locals[i], false, false);
}
int num_locals = func_upvalues->num_locals;
if(num_params < func_decl->num_params){
for(; i < func_decl->num_params; i++){
setPropertyValue(args, PropertyIndex(func_decl->locals[i].name.string, PropertyIndex::KeepStringIndex()), Value(), false, false);
}
}else{
for(i = 0; i < stack_func->num_extra_params; i++){
setPropertyValue(args, Value(args->table ? args->table->next_index : 0), func_upvalues->locals[i + num_locals], false, false);
}
}
}
void OS::Core::pushRestArguments(StackFunction * stack_func)
{
if(!stack_func->rest_arguments){
GCArrayValue * args = pushArrayValue();
Upvalues * func_upvalues = stack_func->locals;
int num_locals = func_upvalues->num_locals;
for(int i = 0; i < stack_func->num_extra_params; i++){
allocator->vectorAddItem(args->values, func_upvalues->locals[i + num_locals] OS_DBG_FILEPOS);
}
stack_func->rest_arguments = args;
}else{
pushValue(stack_func->rest_arguments);
}
}
void OS::Core::pushBackTrace(int skip_funcs, int max_trace_funcs)
{
GCArrayValue * arr = pushArrayValue();
String function_str(allocator, OS_TEXT("function"));
String name_str(allocator, OS_TEXT("name"));
String file_str(allocator, OS_TEXT("file"));
String line_str(allocator, OS_TEXT("line"));
String pos_str(allocator, OS_TEXT("pos"));
String token_str(allocator, OS_TEXT("token"));
String object_str(allocator, OS_TEXT("object"));
String arguments_str(allocator, OS_TEXT("arguments"));
String core_str(allocator, OS_TEXT("<<CORE>>"));
String lambda_str(allocator, OS_TEXT("<<lambda>>"));
for(int i = call_stack_funcs.count-1-skip_funcs; i >= 0 && arr->values.count < max_trace_funcs; i--){
StackFunction * stack_func = call_stack_funcs.buf + i;
Program * prog = stack_func->func->prog;
if(!stack_func->func->name && !prog->filename.getDataSize()){
continue;
}
GCObjectValue * obj = pushObjectValue();
setPropertyValue(obj, PropertyIndex(name_str, PropertyIndex::KeepStringIndex()), stack_func->func->name ? stack_func->func->name : lambda_str.string, false, false);
setPropertyValue(obj, PropertyIndex(function_str, PropertyIndex::KeepStringIndex()), stack_func->func, false, false);
const String& filename = prog->filename.getDataSize() ? prog->filename : core_str;
setPropertyValue(obj, PropertyIndex(file_str, PropertyIndex::KeepStringIndex()), filename.string, false, false);
Program::DebugInfoItem * debug_info = NULL;
if(prog->filename.getDataSize() && prog->debug_info.count > 0){
int opcode_pos = stack_func->opcodes.getPos() + stack_func->func->func_decl->opcodes_pos;
debug_info = prog->getDebugInfo(opcode_pos);
}
setPropertyValue(obj, PropertyIndex(line_str, PropertyIndex::KeepStringIndex()), debug_info ? debug_info->line : Value(), false, false);
setPropertyValue(obj, PropertyIndex(pos_str, PropertyIndex::KeepStringIndex()), debug_info ? debug_info->pos : Value(), false, false);
setPropertyValue(obj, PropertyIndex(token_str, PropertyIndex::KeepStringIndex()), debug_info ? debug_info->token.string : Value(), false, false);
setPropertyValue(obj, PropertyIndex(object_str, PropertyIndex::KeepStringIndex()), stack_func->self, false, false);
pushArgumentsWithNames(stack_func);
setPropertyValue(obj, PropertyIndex(arguments_str, PropertyIndex::KeepStringIndex()), stack_values.lastElement(), false, false);
pop(); // remove args
setPropertyValue(arr, Value(arr->values.count), obj, false, false);
pop(); // remove obj
}
}
int OS::Core::call(int params, int ret_values, GCValue * self_for_proto, bool allow_only_enter_func)
{
if(terminated){
error(OS_E_ERROR, OS_TEXT("ObjectScript is terminated, you could reset terminate state using OS::resetTerminated if necessary"));
pop(params + 2);
return syncRetValues(ret_values, 0);
}
if(stack_values.count >= 2+params){
int end_stack_size = stack_values.count-2-params;
Value func_value = stack_values[stack_values.count-2-params];
switch(func_value.type){
case OS_VALUE_TYPE_FUNCTION:
{
Value self = stack_values[stack_values.count-1-params];
enterFunction(func_value.v.func, self, self_for_proto, params, 2, ret_values);
if(allow_only_enter_func){
return 0;
}
ret_values = execute();
OS_ASSERT(stack_values.count == end_stack_size + ret_values);
return ret_values;
}
case OS_VALUE_TYPE_CFUNCTION:
{
int stack_size_without_params = getStackOffs(-2-params);
GCCFunctionValue * cfunc_value = func_value.v.cfunc;
if(cfunc_value->num_closure_values > 0){
reserveStackValues(stack_values.count + cfunc_value->num_closure_values);
Value * closure_values = (Value*)(cfunc_value + 1);
OS_MEMCPY(stack_values.buf + stack_values.count, closure_values, sizeof(Value)*cfunc_value->num_closure_values);
stack_values.count += cfunc_value->num_closure_values;
}
int func_ret_values = cfunc_value->func(allocator, params, cfunc_value->num_closure_values, ret_values, cfunc_value->user_param);
#if 0
if(cfunc_value->num_closure_values > 0){
Value * closure_values = (Value*)(cfunc_value + 1);
OS_MEMCPY(closure_values, stack_values.buf + stack_values.count, sizeof(Value)*cfunc_value->num_closure_values);
}
#endif
int remove_values = getStackOffs(-func_ret_values) - stack_size_without_params;
OS_ASSERT(remove_values >= 0);
removeStackValues(stack_size_without_params, remove_values);
ret_values = syncRetValues(ret_values, func_ret_values);
OS_ASSERT(stack_values.count == end_stack_size + ret_values);
return ret_values;
}
case OS_VALUE_TYPE_OBJECT:
{
GCValue * object = initObjectInstance(pushObjectValue(func_value.v.value));
object->is_object_instance = true;
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, func_value, PropertyIndex(strings->__construct, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
pushValue(func);
pushValue(object);
moveStackValues(-3, 3, -3-params);
call(params, 1);
// 4 values in stack here
object = stack_values.lastElement().getGCValue();
if(object){
stack_values.count -= 3;
stack_values.lastElement() = object;
return syncRetValues(ret_values, 1);
}else{
pop(4);
return syncRetValues(ret_values, 0);
}
}
removeStackValues(-3, 2);
return syncRetValues(ret_values, 1);
}
case OS_VALUE_TYPE_USERDATA:
case OS_VALUE_TYPE_USERPTR:
{
bool prototype_enabled = true;
Value func;
if(getPropertyValue(func, func_value, PropertyIndex(strings->__construct, PropertyIndex::KeepStringIndex()), prototype_enabled)
&& func.isFunction())
{
stack_values[stack_values.count-2-params] = func;
stack_values[stack_values.count-1-params] = func_value;
return call(params, ret_values); // request result
}
break;
}
}
}
// OS_ASSERT(false);
pop(params + 2);
return syncRetValues(ret_values, 0);
}
bool OS::compileFile(const String& p_filename, bool required)
{
String filename = resolvePath(p_filename);
String compiled_filename = getCompiledFilename(filename);
bool sourcecode_file_exist = isFileExist(filename);
bool compiled_file_exist = isFileExist(compiled_filename);
if(compiled_file_exist && sourcecode_file_exist){
if(core->settings.primary_compiled_file || checkFileUsage(filename, compiled_filename) == LOAD_COMPILED_FILE){
sourcecode_file_exist = false;
}else{
compiled_file_exist = false;
}
}
if(!sourcecode_file_exist && !compiled_file_exist){
if(required){
core->error(OS_E_ERROR, String::format(this, OS_TEXT("required filename %s is not exist"), p_filename.toChar()));
return false;
}
core->error(OS_E_WARNING, String::format(this, OS_TEXT("filename %s is not exist"), p_filename.toChar()));
return false;
}
if(!sourcecode_file_exist){
OS_ASSERT(compiled_file_exist);
Core::Program * prog = new (malloc(sizeof(Core::Program) OS_DBG_FILEPOS)) Core::Program(this);
prog->filename = compiled_filename;
Core::FileStreamReader prog_file_reader(this, compiled_filename);
Core::MemStreamWriter prog_file_data(this);
prog_file_data.writeFromStream(&prog_file_reader);
Core::MemStreamReader prog_reader(NULL, prog_file_data.buffer.buf, prog_file_data.getSize());
String debug_info_filename = getDebugInfoFilename(filename);
if(isFileExist(debug_info_filename)){
Core::FileStreamReader debug_info_file_reader(this, debug_info_filename);
Core::MemStreamWriter debug_info_file_data(this);
debug_info_file_data.writeFromStream(&debug_info_file_reader);
Core::MemStreamReader debug_info_reader(NULL, debug_info_file_data.buffer.buf, debug_info_file_data.getSize());
if(!prog->loadFromStream(&prog_reader, &debug_info_reader)){
prog->release();
return false;
}
}else if(!prog->loadFromStream(&prog_reader, NULL)){
prog->release();
return false;
}
prog->pushStartFunction();
prog->release();
return true;
}
Core::FileStreamReader file(this, filename);
if(!file.f){
core->error(OS_E_ERROR, String::format(this, OS_TEXT("error open filename %s"), p_filename.toChar()));
return false;
}
Core::MemStreamWriter file_data(this);
file_data.writeFromStream(&file);
Core::Tokenizer tokenizer(this);
tokenizer.parseText((OS_CHAR*)file_data.buffer.buf, file_data.buffer.count, filename);
Core::Compiler compiler(&tokenizer);
return compiler.compile();
}
bool OS::compile(const String& str)
{
if(str.getDataSize() == 0){
return false;
}
Core::Tokenizer tokenizer(this);
tokenizer.parseText(str.toChar(), str.getLen(), String(this));
Core::Compiler compiler(&tokenizer);
return compiler.compile();
}
bool OS::compile()
{
String str = toString(-1);
pop(1);
return compile(str);
}
int OS::call(int params, int ret_values)
{
return core->call(params, ret_values);
}
int OS::eval(const OS_CHAR * str, int params, int ret_values)
{
return eval(String(this, str), params, ret_values);
}
int OS::eval(const String& str, int params, int ret_values)
{
compile(str);
pushNull();
move(-2, 2, -2-params);
return core->call(params, ret_values);
}
int OS::require(const OS_CHAR * filename, bool required, int ret_values)
{
return require(String(this, filename), required, ret_values);
}
int OS::require(const String& filename, bool required, int ret_values)
{
getGlobal(OS_TEXT("require"));
pushGlobals();
pushString(filename);
pushBool(required);
return call(2, ret_values);
}
int OS::getSetting(OS_ESettings setting)
{
switch(setting){
case OS_SETTING_CREATE_DEBUG_INFO:
return core->settings.create_debug_info;
case OS_SETTING_CREATE_DEBUG_OPCODES:
return core->settings.create_debug_opcodes;
case OS_SETTING_CREATE_COMPILED_FILE:
return core->settings.create_compiled_file;
case OS_SETTING_PRIMARY_COMPILED_FILE:
return core->settings.primary_compiled_file;
}
return -1;
}
int OS::setSetting(OS_ESettings setting, int value)
{
struct Lib {
static int ret(bool& cur_value, int new_value)
{
int old = cur_value;
cur_value = new_value ? true : false;
return old;
}
};
switch(setting){
case OS_SETTING_CREATE_DEBUG_INFO:
return Lib::ret(core->settings.create_debug_info, value);
case OS_SETTING_CREATE_DEBUG_OPCODES:
return Lib::ret(core->settings.create_debug_opcodes, value);
case OS_SETTING_CREATE_COMPILED_FILE:
return Lib::ret(core->settings.create_compiled_file, value);
case OS_SETTING_PRIMARY_COMPILED_FILE:
return Lib::ret(core->settings.primary_compiled_file, value);
}
return -1;
}
int OS::gc()
{
return core->gcStep();
}
void OS::gcFull()
{
core->gcFull();
}
void OS::triggerError(int code, const OS_CHAR * message)
{
core->error(code, message);
}
void OS::triggerError(int code, const String& message)
{
core->error(code, message);
}
void OS::triggerError(const OS_CHAR * message)
{
core->error(OS_E_ERROR, message);
}
void OS::triggerError(const String& message)
{
core->error(OS_E_ERROR, message);
}
// =====================================================================
// =====================================================================
// =====================================================================
#define OS_QSORT_CUTOFF 8
static void qsortSwap(char *a, char *b, unsigned width)
{
char tmp;
if(a != b) {
if(width == sizeof(void*)){
void * tmp = *(void**)a;
*(void**)a = *(void**)b;
*(void**)b = tmp;
return;
}
if(width >= 16 && width <= 256){
void * tmp = alloca(width);
OS_MEMCPY(tmp, a, width);
OS_MEMCPY(a, b, width);
OS_MEMCPY(b, tmp, width);
return;
}
while(width--){
tmp = *a;
*a++ = *b;
*b++ = tmp;
}
}
}
static void qsortShortsort(OS * os, char *lo, char *hi, unsigned width, int (*comp)(OS*, const void *, const void *, void*), void * user_params)
{
char *p, *max;
while (hi > lo) {
max = lo;
for (p = lo + width; p <= hi; p += width) if (comp(os, p, max, user_params) > 0) max = p;
qsortSwap(max, hi, width);
hi -= width;
}
}
void OS::qsort(void *base, unsigned num, unsigned width, int (*comp)(OS*, const void *, const void *, void*), void * user_params)
{
char *lo, *hi;
char *mid;
char *l, *h;
unsigned size;
char *lostk[30], *histk[30];
int stkptr;
if (num < 2 || width == 0) return;
stkptr = 0;
lo = (char*)base;
hi = (char*)base + width * (num - 1);
recurse:
size = (hi - lo) / width + 1;
if (size <= OS_QSORT_CUTOFF) {
qsortShortsort(this, lo, hi, width, comp, user_params);
} else {
mid = lo + (size / 2) * width;
qsortSwap(mid, lo, width);
l = lo;
h = hi + width;
for (;;) {
do { l += width; } while (l <= hi && comp(this, l, lo, user_params) <= 0);
do { h -= width; } while (h > lo && comp(this, h, lo, user_params) >= 0);
if (h < l) break;
qsortSwap(l, h, width);
}
qsortSwap(lo, h, width);
if (h - 1 - lo >= hi - l) {
if (lo + width < h) {
lostk[stkptr] = lo;
histk[stkptr] = h - width;
++stkptr;
}
if (l < hi) {
lo = l;
goto recurse;
}
} else {
if (l < hi) {
lostk[stkptr] = l;
histk[stkptr] = hi;
++stkptr;
}
if (lo + width < h) {
hi = h - width;
goto recurse;
}
}
}
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse;
}
}
// =====================================================================
// =====================================================================
// =====================================================================
static FunctionDataChain * function_data_first = NULL;
FunctionDataChain::FunctionDataChain()
{
next = function_data_first;
function_data_first = this;
}
FunctionDataChain::~FunctionDataChain()
{
}
void ObjectScript::finalizeAllBinds()
{
while(function_data_first){
FunctionDataChain * cur = function_data_first;
function_data_first = cur->next;
delete cur;
}
}
struct FunctionDataFinalizer
{
~FunctionDataFinalizer(){ ObjectScript::finalizeAllBinds(); }
} __functionDataFinalizer__;
// =====================================================================
// =====================================================================
// =====================================================================