// ---------------------------------------------------------------------------- // Description : Javascript interpreter // ---------------------------------------------------------------------------- // (c) Copyright 2000 by iXiONmedia, all rights reserved. // ---------------------------------------------------------------------------- #include #include #include #include #include #include #include using namespace std; using namespace ixion; using namespace javascript; // value ---------------------------------------------------------------------- string value::toString() const { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, (valueType2string(getType())+string(_("-> string"))).c_str()) } int value::toInt() const { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, (valueType2string(getType())+_(" -> int")).c_str()) } double value::toFloat() const { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, (valueType2string(getType())+_(" -> float")).c_str()) } bool value::toBoolean() const { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, (valueType2string(getType())+_(" -> bool")).c_str()) } string value::stringify() const { try { return toString(); } catch (...) { return string("#<")+valueType2string(getType())+">"; } } ref value::eliminateWrappers() { return this; } ref value::duplicate() { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": duplication")).c_str()) } ref value::lookup(string const &identifier) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": lookup of ")+identifier).c_str()) } ref value::subscript(value const &index) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": subscript")).c_str()) } ref value::call(parameter_list const ¶meters) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": call")).c_str()) } ref value::callAsMethod(ref instance,parameter_list const ¶meters) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": call as method")).c_str()) } ref value::construct(parameter_list const ¶meters) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": construction")).c_str()) } ref value::assign(ref op2) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": assignment")).c_str()) } ref value::operatorUnary(operator_id op) const { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+_(": operator ")+operator2string(op)).c_str()) } ref value::operatorBinary(operator_id op,ref op2) const { if (op == OP_EQUAL) { if (getType() == VT_NULL) return makeConstant(op2->getType() == VT_NULL); if (op2->getType() == VT_NULL) return makeConstant(getType() == VT_NULL); } if (op == OP_NOT_EQUAL) { if (getType() == VT_NULL) return makeConstant(op2->getType() != VT_NULL); if (op2->getType() == VT_NULL) return makeConstant(getType() != VT_NULL); } EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+" "+string(operator2string(op))+" "+valueType2string(op2->getType())).c_str()) } ref value::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { if (op == OP_LOGICAL_OR) return makeConstant(toBoolean() || op2.evaluate(ctx)->toBoolean()); if (op == OP_LOGICAL_AND) return makeConstant(toBoolean() && op2.evaluate(ctx)->toBoolean()); EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (string(operator2string(op))+_(" on ")+valueType2string(getType())).c_str()) } ref value::operatorUnaryModifying(operator_id op) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (string(operator2string(op))+_(" on ")+valueType2string(getType())).c_str()) } ref value::operatorBinaryModifying(operator_id op,ref op2) { EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, (valueType2string(getType())+" "+string(operator2string(op))+" "+valueType2string(op2->getType())).c_str()) } value::operator_id value::token2operator(scanner::token const &token,bool unary,bool prefix) { switch (token.Type) { case TT_JS_INCREMENT: return prefix ? OP_PRE_INCREMENT : OP_POST_INCREMENT; case TT_JS_DECREMENT: return prefix ? OP_PRE_DECREMENT : OP_POST_DECREMENT; case '+': return unary ? OP_UNARY_PLUS : OP_PLUS; case '-': return unary ? OP_UNARY_MINUS : OP_MINUS; case '!': return OP_LOG_NOT; case '~': return OP_BIN_NOT; case TT_JS_PLUS_ASSIGN: return OP_PLUS_ASSIGN; case TT_JS_MINUS_ASSIGN: return OP_MINUS_ASSIGN; case TT_JS_MULTIPLY_ASSIGN: return OP_MUTLIPLY_ASSIGN; case TT_JS_DIVIDE_ASSIGN: return OP_DIVIDE_ASSIGN; case TT_JS_MODULO_ASSIGN: return OP_MODULO_ASSIGN; case TT_JS_BIT_AND_ASSIGN: return OP_BIT_AND_ASSIGN; case TT_JS_BIT_OR_ASSIGN: return OP_BIT_OR_ASSIGN; case TT_JS_BIT_XOR_ASSIGN: return OP_BIT_XOR_ASSIGN; case TT_JS_LEFT_SHIFT_ASSIGN: return OP_LEFT_SHIFT_ASSIGN; case TT_JS_RIGHT_SHIFT_ASSIGN: return OP_RIGHT_SHIFT_ASSIGN; case '*': return OP_MULTIPLY; case '/': return OP_DIVIDE; case '%': return OP_MODULO; case '&': return OP_BIT_AND; case '|': return OP_BIT_OR; case '^': return OP_BIT_XOR; case TT_JS_LEFT_SHIFT: return OP_LEFT_SHIFT; case TT_JS_RIGHT_SHIFT: return OP_RIGHT_SHIFT; case TT_JS_LOGICAL_OR: return OP_LOGICAL_OR; case TT_JS_LOGICAL_AND: return OP_LOGICAL_AND; case TT_JS_EQUAL: return OP_EQUAL; case TT_JS_NOT_EQUAL: return OP_NOT_EQUAL; case TT_JS_IDENTICAL: return OP_IDENTICAL; case TT_JS_NOT_IDENTICAL: return OP_NOT_IDENTICAL; case TT_JS_LESS_EQUAL: return OP_LESS_EQUAL; case TT_JS_GREATER_EQUAL: return OP_GREATER_EQUAL; case '<': return OP_LESS; case '>': return OP_GREATER; default: EXJS_THROWINFO(ECJS_UNKNOWN_OPERATOR,token.Text.c_str()) } } string value::operator2string(operator_id op) { switch (op) { case OP_PRE_INCREMENT: return _("prefix ++"); case OP_POST_INCREMENT: return _("postfix ++"); case OP_PRE_DECREMENT: return _("prefix --"); case OP_POST_DECREMENT: return _("postfix ++"); case OP_UNARY_PLUS: return _("unary +"); case OP_UNARY_MINUS: return _("unary -"); case OP_LOG_NOT: return "!"; case OP_BIN_NOT: return "~"; case OP_PLUS_ASSIGN: return "+="; case OP_MINUS_ASSIGN: return "-="; case OP_MUTLIPLY_ASSIGN: return "*="; case OP_DIVIDE_ASSIGN: return "/="; case OP_MODULO_ASSIGN: return "%="; case OP_BIT_AND_ASSIGN: return "&="; case OP_BIT_OR_ASSIGN: return "|="; case OP_BIT_XOR_ASSIGN: return "^="; case OP_LEFT_SHIFT_ASSIGN: return "<<="; case OP_RIGHT_SHIFT_ASSIGN: return ">>="; case OP_PLUS: return "+"; case OP_MINUS: return "-"; case OP_MULTIPLY: return "*"; case OP_DIVIDE: return "/"; case OP_MODULO: return "%"; case OP_BIT_AND: return "&"; case OP_BIT_OR: return "|"; case OP_BIT_XOR: return "^"; case OP_LEFT_SHIFT: return "<<"; case OP_RIGHT_SHIFT: return ">>"; case OP_LOGICAL_OR: return "|"; case OP_LOGICAL_AND: return "&"; case OP_EQUAL: return "=="; case OP_NOT_EQUAL: return "!="; case OP_IDENTICAL: return "==="; case OP_NOT_IDENTICAL: return "!=="; case OP_LESS_EQUAL: return "<="; case OP_GREATER_EQUAL: return ">="; case OP_LESS: return "<"; case OP_GREATER: return ">"; case OP_ASSIGN: return "="; default: EXJS_THROW(ECJS_UNKNOWN_OPERATOR) } } string value::valueType2string(value_type vt) { switch (vt) { case VT_UNDEFINED: return _("undefined"); case VT_NULL: return _("null"); case VT_INTEGER: return _("integer"); case VT_FLOATING_POINT: return _("floating point"); case VT_STRING: return _("string"); case VT_FUNCTION: return _("function"); case VT_OBJECT: return _("object"); case VT_BUILTIN: return _("built-in object"); case VT_HOST: return _("host object"); case VT_SCOPE: return _("scope"); case VT_BOUND_METHOD: return _("bound method"); case VT_TYPE: return _("type"); default: return _("unknown value type"); } } // value_with_methods --------------------------------------------------------- value_with_methods::bound_method:: bound_method(string const &identifier,ref parent) : Identifier(identifier),Parent(parent) { } ref value_with_methods::bound_method::duplicate() { // bound_methods are immutable return this; } ref value_with_methods::bound_method:: call(parameter_list const ¶meters) { return Parent->callMethod(Identifier,parameters); } ref value_with_methods:: lookup(string const &identifier) { ref result = new bound_method(identifier,this); return result; } // null ----------------------------------------------------------------------- value::value_type null::getType() const { return VT_NULL; } bool null::toBoolean() const { return false; } ref null::duplicate() { return makeNull(); } // const_floating_point ------------------------------------------------------- const_floating_point::const_floating_point(double value) : Value(value) { } value::value_type const_floating_point::getType() const { return VT_FLOATING_POINT; } int const_floating_point::toInt() const { return (int) Value; } double const_floating_point::toFloat() const { return (double) Value; } bool const_floating_point::toBoolean() const { return (bool) Value; } string const_floating_point::toString() const { return float2dec(Value); } ref const_floating_point::duplicate() { return makeValue(Value); } ref const_floating_point:: callMethod(string const &identifier,parameter_list const ¶meters) { IXLIB_JS_IF_METHOD("toInt",0,0) return makeConstant((signed long) Value); IXLIB_JS_IF_METHOD("toFloat",0,0) return makeConstant(Value); IXLIB_JS_IF_METHOD("toString",0,1) { unsigned radix = 10; if (parameters.size() == 1) radix = parameters[0]->toInt(); if (radix == 10) return makeConstant(float2dec(Value)); else return makeConstant(signed2base((int) Value,0,radix)); } IXLIB_JS_IF_METHOD("toFixed",0,1) { unsigned digits = 0; if (parameters.size() == 1) digits = parameters[0]->toInt(); char buffer[1024]; sprintf(buffer,("%."+unsigned2dec(digits)+"f").c_str(),Value); return makeConstant(buffer); } IXLIB_JS_IF_METHOD("toExponential",0,1) { char buffer[1024]; if (parameters.size() == 1) sprintf(buffer,("%."+unsigned2dec(parameters[0]->toInt())+"e").c_str(),Value); else sprintf(buffer,"%e",Value); return makeConstant(buffer); } IXLIB_JS_IF_METHOD("toPrecision",0,1) { if (parameters.size() == 1) return makeConstant(float2dec(Value,parameters[0]->toInt())); else return makeConstant(float2dec(Value)); } EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("float." + identifier).c_str()) } ref const_floating_point::operatorUnary(operator_id op) const { switch (op) { case OP_UNARY_PLUS: return makeConstant(+Value); case OP_UNARY_MINUS: return makeConstant(-Value); case OP_LOG_NOT: return makeConstant(!Value); case OP_BIN_NOT: return makeConstant(~ (long) Value); default: return super::operatorUnary(op); } } ref const_floating_point::operatorBinary(operator_id op,ref op2) const { #define FLOAT_OPERATOR(OP) \ if (op2->getType() == VT_FLOATING_POINT || op2->getType() == VT_INTEGER) \ return makeConstant(Value OP op2->toFloat()); #define PASS_UP \ return super::operatorBinary(op,op2); switch (op) { case OP_PLUS: FLOAT_OPERATOR(+) PASS_UP case OP_MINUS: FLOAT_OPERATOR(-) PASS_UP case OP_MULTIPLY: FLOAT_OPERATOR(*) PASS_UP case OP_DIVIDE: if (op2->getType() == VT_FLOATING_POINT || op2->getType() == VT_INTEGER) { double op2value = op2->toFloat(); if (op2value == 0) EXJS_THROW(ECJS_DIVISION_BY_ZERO) return makeConstant(Value / op2value); } PASS_UP case OP_EQUAL: FLOAT_OPERATOR(==) PASS_UP case OP_NOT_EQUAL: FLOAT_OPERATOR(!=) PASS_UP case OP_LESS_EQUAL: FLOAT_OPERATOR(<=) PASS_UP case OP_GREATER_EQUAL: FLOAT_OPERATOR(>=) PASS_UP case OP_LESS: FLOAT_OPERATOR(<) PASS_UP case OP_GREATER: FLOAT_OPERATOR(>) PASS_UP default: return super::operatorBinary(op,op2); } #undef FLOAT_OPERATOR #undef PASS_UP } // floating_point ------------------------------------------------------------- floating_point::floating_point(double val) : const_floating_point(val) { } ref floating_point::operatorUnaryModifying(operator_id op) { switch (op) { case OP_PRE_INCREMENT: Value++; return ref(this); case OP_POST_INCREMENT: // *** FIXME this should be an lvalue return makeConstant(Value++); case OP_PRE_DECREMENT: Value--; return ref(this); case OP_POST_DECREMENT: // *** FIXME this should be an lvalue return makeConstant(Value--); default: return super::operatorUnaryModifying(op); } } ref floating_point::operatorBinaryModifying(operator_id op,ref op2) { int val; switch (op) { case OP_PLUS_ASSIGN: Value += op2->toFloat(); return ref(this); case OP_MINUS_ASSIGN: Value -= op2->toFloat(); return ref(this); case OP_MUTLIPLY_ASSIGN: Value *= op2->toFloat(); return ref(this); case OP_DIVIDE_ASSIGN: { double op2value = op2->toFloat(); if (op2value == 0) EXJS_THROW(ECJS_DIVISION_BY_ZERO) Value /= op2value; return ref(this); } case OP_MODULO_ASSIGN: val = (int) Value; val %= (int) op2->toFloat(); Value = val; return ref(this); case OP_BIT_AND_ASSIGN: val = (int) Value; val &= (int) op2->toFloat(); Value = val; return ref(this); case OP_BIT_OR_ASSIGN: val = (int) Value; val |= (int) op2->toFloat(); Value = val; return ref(this); case OP_BIT_XOR_ASSIGN: val = (int) Value; val ^= (int) op2->toFloat(); Value = val; return ref(this); case OP_LEFT_SHIFT_ASSIGN: val = (int) Value; val <<= (int) op2->toFloat(); Value = val; return ref(this); case OP_RIGHT_SHIFT_ASSIGN: val = (int) Value; val >>= (int) op2->toFloat(); Value = val; return ref(this); default: return super::operatorBinaryModifying(op,op2); } } // const_integer -------------------------------------------------------------- const_integer::const_integer(long value) : Value(value) { } value::value_type const_integer::getType() const { return VT_INTEGER; } int const_integer::toInt() const { return (int) Value; } double const_integer::toFloat() const { return (double) Value; } bool const_integer::toBoolean() const { return (bool) Value; } string const_integer::toString() const { return signed2dec(Value); } ref const_integer::duplicate() { return makeValue(Value); } ref const_integer:: callMethod(string const &identifier,parameter_list const ¶meters) { IXLIB_JS_IF_METHOD("toInt",0,0) return makeConstant(Value); IXLIB_JS_IF_METHOD("toFloat",0,0) return makeConstant((double) Value); IXLIB_JS_IF_METHOD("toString",0,1) { unsigned radix = 10; if (parameters.size() == 1) radix = parameters[0]->toInt(); return makeConstant(signed2base(Value,0,radix)); } EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("integer." + identifier).c_str()) } ref const_integer::operatorUnary(operator_id op) const { switch (op) { case OP_UNARY_PLUS: return makeConstant(+Value); case OP_UNARY_MINUS: return makeConstant(-Value); case OP_LOG_NOT: return makeConstant(!Value); case OP_BIN_NOT: return makeConstant(~ (long) Value); default: return super::operatorUnary(op); } } ref const_integer::operatorBinary(operator_id op,ref op2) const { #define FLOAT_OPERATOR(OP) \ if (op2->getType() == VT_FLOATING_POINT) \ return makeConstant(toFloat() OP op2->toFloat()); #define INT_OPERATOR(OP) \ if (op2->getType() == VT_INTEGER) \ return makeConstant(Value OP op2->toInt()); #define PASS_UP \ return super::operatorBinary(op,op2); switch (op) { case OP_PLUS: FLOAT_OPERATOR(+) INT_OPERATOR(+) PASS_UP case OP_MINUS: FLOAT_OPERATOR(-) INT_OPERATOR(-) PASS_UP case OP_MULTIPLY: FLOAT_OPERATOR(*) INT_OPERATOR(*) PASS_UP case OP_DIVIDE: if (op2->toFloat() == 0) EXJS_THROW(ECJS_DIVISION_BY_ZERO) FLOAT_OPERATOR(/) INT_OPERATOR(/) PASS_UP case OP_MODULO: INT_OPERATOR(%) PASS_UP case OP_BIT_AND: INT_OPERATOR(&) PASS_UP case OP_BIT_OR: INT_OPERATOR(|) PASS_UP case OP_BIT_XOR: INT_OPERATOR(^) PASS_UP case OP_LEFT_SHIFT: INT_OPERATOR(<<) PASS_UP case OP_RIGHT_SHIFT: INT_OPERATOR(>>) PASS_UP case OP_EQUAL: FLOAT_OPERATOR(==) INT_OPERATOR(==) PASS_UP case OP_NOT_EQUAL: FLOAT_OPERATOR(!=) INT_OPERATOR(!=) PASS_UP case OP_LESS_EQUAL: FLOAT_OPERATOR(<=) INT_OPERATOR(<=) PASS_UP case OP_GREATER_EQUAL: FLOAT_OPERATOR(>=) INT_OPERATOR(>=) PASS_UP case OP_LESS: FLOAT_OPERATOR(<) INT_OPERATOR(<) PASS_UP case OP_GREATER: FLOAT_OPERATOR(>) INT_OPERATOR(>) PASS_UP default: PASS_UP } #undef FLOAT_OPERATOR #undef INT_OPERATOR #undef PASS_UP } // integer -------------------------------------------------------------------- integer::integer(long val) : const_integer(val) { } ref integer::operatorUnaryModifying(operator_id op) { switch (op) { case OP_PRE_INCREMENT: Value++; return ref(this); case OP_POST_INCREMENT: // *** FIXME this should be an lvalue return makeConstant(Value++); case OP_PRE_DECREMENT: Value--; return ref(this); case OP_POST_DECREMENT: // *** FIXME this should be an lvalue return makeConstant(Value--); default: return super::operatorUnaryModifying(op); } } ref integer::operatorBinaryModifying(operator_id op,ref op2) { int val; int op2value = op2->toInt(); switch (op) { case OP_PLUS_ASSIGN: Value += op2value; return ref(this); case OP_MINUS_ASSIGN: Value -= op2value; return ref(this); case OP_MUTLIPLY_ASSIGN: Value *= op2value; return ref(this); case OP_DIVIDE_ASSIGN: if (op2value == 0) EXJS_THROW(ECJS_DIVISION_BY_ZERO) Value /= op2value; return ref(this); case OP_MODULO_ASSIGN: val = Value; val %= op2value; Value = val; return ref(this); case OP_BIT_AND_ASSIGN: val = Value; val &= op2value; Value = val; return ref(this); case OP_BIT_OR_ASSIGN: val = Value; val |= op2value; Value = val; return ref(this); case OP_BIT_XOR_ASSIGN: val = Value; val ^= op2value; Value = val; return ref(this); case OP_LEFT_SHIFT_ASSIGN: val = Value; val <<= op2value; Value = val; return ref(this); case OP_RIGHT_SHIFT_ASSIGN: val = Value; val >>= op2value; Value = val; return ref(this); default: return super::operatorBinaryModifying(op,op2); } } // js_string ------------------------------------------------------------------ js_string::js_string(string const &value) : Value(value) { } value::value_type js_string::getType() const { return VT_STRING; } string js_string::toString() const { return Value; } bool js_string::toBoolean() const { return Value.size() == 0; } string js_string::stringify() const { return '"'+Value+'"'; } ref js_string::duplicate() { return makeValue(Value); } ref js_string::lookup(string const &identifier) { if (identifier == "length") return makeConstant(Value.size()); return super::lookup(identifier); } ref js_string::callMethod(string const &identifier,parameter_list const ¶meters) { IXLIB_JS_IF_METHOD("toString",0,0) return makeConstant(Value); IXLIB_JS_IF_METHOD("charAt",1,1) return makeConstant(string(1,Value.at(parameters[0]->toInt()))); IXLIB_JS_IF_METHOD("charCodeAt",1,1) return makeConstant(Value.at(parameters[0]->toInt())); if (identifier == "concat") { string result = Value; FOREACH_CONST(first,parameters,parameter_list) Value += (*first)->toString(); return makeConstant(Value); } IXLIB_JS_IF_METHOD("indexOf",1,2) { string::size_type startpos = 0; if (parameters.size() == 2) startpos = parameters[1]->toInt(); string::size_type result = Value.find(parameters[0]->toString(),startpos); if (result == string::npos) return makeConstant(-1); else return makeConstant(result); } IXLIB_JS_IF_METHOD("lastIndexOf",1,2) { string::size_type startpos = string::npos; if (parameters.size() == 2) startpos = parameters[1]->toInt(); string::size_type result = Value.rfind(parameters[0]->toString(),startpos); if (result == string::npos) return makeConstant(-1); else return makeConstant(result); } // *** FIXME we need proper regexps IXLIB_JS_IF_METHOD("match",1,1) { regex_string re(parameters[0]->toString()); return makeConstant(re.matchAt(Value,0)); } IXLIB_JS_IF_METHOD("replace",2,2) { regex_string re(parameters[0]->toString()); return makeConstant(re.replaceAll(Value,parameters[1]->toString())); } IXLIB_JS_IF_METHOD("search",1,1) { regex_string re(parameters[0]->toString()); return makeConstant(re.match(Value)); } IXLIB_JS_IF_METHOD("slice",2,2) { TIndex start = parameters[0]->toInt(),end = parameters[1]->toInt(); return makeConstant(string(Value,start,end-start)); } /* Allow 2 parameters here, the first one is the separator and the second one is the maximum allowed number of elements of the resulting array if the function is called without a parameter the string is split into a character array. The second parameter sets the maximum number of elements of the resulting array. */ IXLIB_JS_IF_METHOD("split", 0, 2 ) { string::size_type start = 0, last = 0; int count = 0; int max = -1; if (parameters.size() == 2) max = parameters[1]->toInt(); string sep; auto_ptr result(new js_array(0)); if (parameters.size() == 0 || (sep = parameters[0]->toString()).empty()) { for (string::size_type i = 0; i < Value.size(); ++i) { string s; s += Value[i]; result->push_back(makeValue(s)); } } else { while (true) { if (max > 0) count++; if (count >= max && max > 0) { result->push_back(makeValue(Value.substr(last))); break; } start = Value.find(sep, last); if (start == string::npos) { result->push_back(makeValue(Value.substr(last))); break; } result->push_back(makeValue(Value.substr(last, start - last))); last = start + sep.size(); } } return result.release(); } IXLIB_JS_IF_METHOD("substring",2,2) { TIndex start = parameters[0]->toInt(),end = parameters[1]->toInt(); if (start > end) swap(start,end); return makeConstant(string(Value,start,end-start)); } IXLIB_JS_IF_METHOD("toLowerCase",0,0) { return makeConstant(lower(Value)); } IXLIB_JS_IF_METHOD("toUpperCase",0,0) { return makeConstant(upper(Value)); } EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("String." + identifier).c_str()) } ref js_string::operatorBinary(operator_id op,ref op2) const { switch (op) { case OP_PLUS: return makeConstant(Value+op2->toString()); case OP_EQUAL: if (op2->getType() == VT_STRING) return makeConstant(Value == op2->toString()); else return super::operatorBinary(op,op2); case OP_NOT_EQUAL: if (op2->getType() == VT_STRING) return makeConstant(Value != op2->toString()); else return super::operatorBinary(op,op2); case OP_LESS_EQUAL: if (op2->getType() == VT_STRING) return makeConstant(Value <= op2->toString()); else return super::operatorBinary(op,op2); case OP_GREATER_EQUAL: if (op2->getType() == VT_STRING) return makeConstant(Value >= op2->toString()); else return super::operatorBinary(op,op2); case OP_LESS: if (op2->getType() == VT_STRING) return makeConstant(Value < op2->toString()); else return super::operatorBinary(op,op2); case OP_GREATER: if (op2->getType() == VT_STRING) return makeConstant(Value > op2->toString()); else return super::operatorBinary(op,op2); default: return super::operatorBinary(op,op2); } } ref js_string::operatorBinaryModifying(operator_id op,ref op2) { switch (op) { case OP_PLUS_ASSIGN: Value += op2->toString(); return ref(this); default: return super::operatorBinaryModifying(op,op2); } } // lvalue --------------------------------------------------------------------- lvalue::lvalue(ref ref) : Reference(ref) { } value::value_type lvalue::getType() const { return Reference->getType(); } string lvalue::toString() const { return Reference->toString(); } int lvalue::toInt() const { return Reference->toInt(); } double lvalue::toFloat() const { return Reference->toFloat(); } bool lvalue::toBoolean() const { return Reference->toBoolean(); } string lvalue::stringify() const { return Reference->stringify(); } ref lvalue::eliminateWrappers() { return Reference.get(); } ref lvalue::duplicate() { return makeLValue(Reference->duplicate()); } ref lvalue::lookup(string const &identifier) { return Reference->lookup(identifier); } ref lvalue::subscript(value const &index) { return Reference->subscript(index); } ref lvalue::call(parameter_list const ¶meters) { return Reference->call(parameters); } ref lvalue::callAsMethod(ref instance,parameter_list const ¶meters) { return Reference->callAsMethod(instance,parameters); } ref lvalue::construct(parameter_list const ¶meters) { return Reference->construct(parameters); } ref lvalue::assign(ref op2) { Reference = op2; return this; } ref lvalue::operatorUnary(operator_id op) const { return Reference->operatorUnary(op); } ref lvalue::operatorBinary(operator_id op,ref op2) const { return Reference->operatorBinary(op,op2); } ref lvalue::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { return Reference->operatorBinaryShortcut(op,op2,ctx); } #define LVALUE_RETURN(VALUE) \ ref __result = VALUE; \ if (__result.get() == Reference.get()) \ return this; \ else \ return __result; ref lvalue::operatorUnaryModifying(operator_id op) { LVALUE_RETURN(Reference->operatorUnaryModifying(op)) } ref lvalue::operatorBinaryModifying(operator_id op,ref op2) { LVALUE_RETURN(Reference->operatorBinaryModifying(op,op2)) } // constant_wrapper ----------------------------------------------------------- constant_wrapper::constant_wrapper(ref val) : Constant(val) { } value::value_type constant_wrapper::getType() const { return Constant->getType(); } string constant_wrapper::toString() const { return Constant->toString(); } int constant_wrapper::toInt() const { return Constant->toInt(); } double constant_wrapper::toFloat() const { return Constant->toFloat(); } bool constant_wrapper::toBoolean() const { return Constant->toBoolean(); } string constant_wrapper::stringify() const { return Constant->stringify(); } ref constant_wrapper::eliminateWrappers() { return Constant.get(); } ref constant_wrapper::duplicate() { return wrapConstant(Constant->duplicate()); } ref constant_wrapper::lookup(string const &identifier) { return Constant->lookup(identifier); } ref constant_wrapper::subscript(value const &index) { return Constant->subscript(index); } ref constant_wrapper::call(parameter_list const ¶meters) const { return Constant->call(parameters); } ref constant_wrapper::callAsMethod(ref instance,parameter_list const ¶meters) { return Constant->callAsMethod(instance,parameters); } ref constant_wrapper::construct(parameter_list const ¶meters) { return Constant->construct(parameters); } ref constant_wrapper::assign(ref value) { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,_("by assignment")) } ref constant_wrapper::operatorUnary(operator_id op) const { return Constant->operatorUnary(op); } ref constant_wrapper::operatorBinary(operator_id op,ref op2) const { return Constant->operatorBinary(op,op2); } ref constant_wrapper::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { return Constant->operatorBinaryShortcut(op,op2,ctx); } ref constant_wrapper::operatorUnaryModifying(operator_id op) { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,operator2string(op).c_str()) } ref constant_wrapper::operatorBinaryModifying(operator_id op,ref op2) { EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,operator2string(op).c_str()) } // list_scope ----------------------------------------------------------------- ref list_scope::lookup(string const &identifier) { member_map::iterator item = MemberMap.find(identifier); if (item != MemberMap.end()) return item->second; FOREACH_CONST(first,SwallowedList,swallowed_list) { try { return (*first)->lookup(identifier); } catch (...) { } } EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) } void list_scope::unite(ref scope) { SwallowedList.push_back(scope); } void list_scope::separate(ref scope) { FOREACH(first,SwallowedList,swallowed_list) { if (*first == scope) { SwallowedList.erase(first); return; } } EXGEN_THROW(EC_ITEMNOTFOUND) } void list_scope::clearScopes() { SwallowedList.clear(); } bool list_scope::hasMember(string const &name) const { member_map::const_iterator item = MemberMap.find(name); return item != MemberMap.end(); } void list_scope::addMember(string const &name,ref member) { if (hasMember(name)) EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_REDECLARE,name.c_str()) MemberMap[name] = member; } void list_scope::removeMember(string const &name) { MemberMap.erase(name); } void list_scope::clearMembers() { MemberMap.clear(); } void list_scope::clear() { clearScopes(); clearMembers(); } // callable_with_parameters --------------------------------------------------- callable_with_parameters::callable_with_parameters(parameter_name_list const &pnames) : ParameterNameList(pnames) { } void callable_with_parameters::addParametersTo(list_scope &scope,parameter_list const ¶meters) const { parameter_list::const_iterator firstp = parameters.begin(), lastp = parameters.end(); FOREACH_CONST(first,ParameterNameList,parameter_name_list) { if (firstp == lastp) scope.addMember(*first,makeLValue(makeNull())); else scope.addMember(*first,makeLValue((*firstp)->eliminateWrappers()->duplicate())); firstp++; } } ref callable_with_parameters::evaluateBody(expression &body,context const &ctx) { ref result; try { result = body.evaluate(ctx); } catch (return_exception &fr) { result = fr.ReturnValue; } catch (break_exception &be) { if (be.HasLabel) EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("break "+be.Label).c_str(),be.Location) else EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"break",be.Location) } catch (continue_exception &ce) { if (ce.HasLabel) EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("continue "+ce.Label).c_str(),ce.Location) else EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"continue",ce.Location) } if (result.get()) return result->eliminateWrappers()->duplicate(); return ref(NULL); } // function ------------------------------------------------------------------- function::function(parameter_name_list const &pnames,ref body,ref lex_scope) : super(pnames),Body(body),LexicalScope(lex_scope) { } ref function::duplicate() { // functions are not mutable return this; } ref function:: call(parameter_list const ¶meters) { ref scope = new list_scope; scope->unite(LexicalScope); addParametersTo(*scope,parameters); return evaluateBody(*Body,context(scope)); // ATTENTION: this is a scope cancellation point. } // method --------------------------------------------------------------------- method::method(parameter_name_list const &pnames,ref body,ref lex_scope) : super(pnames),Body(body),LexicalScope(lex_scope) { } ref method::duplicate() { // methods are not mutable return this; } ref method:: callAsMethod(ref instance,parameter_list const ¶meters) { ref scope = new list_scope; scope->unite(instance); scope->unite(LexicalScope); scope->addMember("this",instance); addParametersTo(*scope,parameters); return evaluateBody(*Body,context(scope)); // ATTENTION: this is a scope cancellation point. } // constructor ---------------------------------------------------------------- constructor::constructor(parameter_name_list const &pnames,ref body,ref lex_scope) : super(pnames),Body(body),LexicalScope(lex_scope) { } ref constructor::duplicate() { // constructors are not mutable return this; } ref constructor:: callAsMethod(ref instance,parameter_list const ¶meters) { ref scope = new list_scope; scope->unite(LexicalScope); scope->unite(instance); scope->addMember("this",instance); addParametersTo(*scope,parameters); return evaluateBody(*Body,context(scope)); // ATTENTION: this is a scope cancellation point. } // js_class ------------------------------------------------------------------- js_class::super_instance_during_construction::super_instance_during_construction( ref super_class) : SuperClass(super_class) { } ref js_class::super_instance_during_construction::call(parameter_list const ¶meters) { if (SuperClassInstance.get()) EXJS_THROW_NO_LOCATION(ECJS_DOUBLE_CONSTRUCTION) SuperClassInstance = SuperClass->construct(parameters); return SuperClassInstance; } ref js_class::super_instance_during_construction::lookup(string const &identifier) { return getSuperClassInstance()->lookup(identifier); } ref js_class::super_instance_during_construction::getSuperClassInstance() { if (SuperClassInstance.get()) return SuperClassInstance; SuperClassInstance = SuperClass->construct(parameter_list()); return SuperClassInstance; } js_class::js_class(ref lex_scope,ref super_class,ref constructor, ref static_method_scope,ref method_scope, ref static_variable_scope,declaration_list const &variable_list) : LexicalScope(lex_scope),SuperClass(super_class),Constructor(constructor), StaticMethodScope(static_method_scope), MethodScope(method_scope),StaticVariableScope(static_variable_scope), VariableList(variable_list) { } ref js_class::duplicate() { // classes are not mutable return this; } ref js_class::lookup(string const &identifier) { try { return lookupLocal(identifier); } catch (...) { } if (SuperClass.get()) return SuperClass->lookup(identifier); EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) } ref js_class::lookupLocal(string const &identifier) { try { return StaticMethodScope->lookup(identifier); } catch (...) { } return StaticVariableScope->lookup(identifier); } ref js_class::construct(parameter_list const ¶meters) { ref vl(new list_scope); ref instance( new js_class_instance(this,MethodScope,vl)); ref construction_scope(new list_scope); construction_scope->unite(LexicalScope); construction_scope->unite(instance); FOREACH_CONST(first,VariableList,declaration_list) (*first)->evaluate(context(vl,construction_scope)); ref temp_super; if (SuperClass.get()) { temp_super = new super_instance_during_construction(SuperClass); vl->addMember("super",temp_super); } if (Constructor.get()) Constructor->callAsMethod(instance,parameters); if (temp_super.get()) { ref super = temp_super->getSuperClassInstance(); vl->removeMember("super"); instance->setSuperClassInstance(super); vl->addMember("super",super); } return instance; } // js_class_instance ---------------------------------------------------------- js_class_instance::bound_method::bound_method(ref instance,ref method) : Instance(instance),Method(method) { } ref js_class_instance::bound_method::call(parameter_list const ¶meters) { return Method->callAsMethod(Instance,parameters); } js_class_instance::js_class_instance(ref cls, ref method_scope,ref variable_scope) : Class(cls),MethodScope(method_scope),VariableScope(variable_scope) { } void js_class_instance::setSuperClassInstance(ref super_class_instance) { SuperClassInstance = super_class_instance; } ref js_class_instance::duplicate() { return this; } ref js_class_instance::lookup(string const &identifier) { try { ref method = MethodScope->lookup(identifier); ref bound = new bound_method(this,method); return bound; } catch (...) { } try { return VariableScope->lookup(identifier); } catch (...) { } try { return Class->lookupLocal(identifier); } catch (...) { } if (SuperClassInstance.get()) return SuperClassInstance->lookup(identifier); EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) } // value creation ------------------------------------------------------------- ref javascript::makeUndefined() { // *** FIXME: this is non-compliant ref result(new null()); return result; } ref javascript::makeNull() { ref result(new null()); return result; } ref javascript::makeValue(signed int val) { ref result(new integer(val)); return result; } ref javascript::makeConstant(signed int val) { ref result(new const_integer(val)); return result; } ref javascript::makeValue(unsigned int val) { ref result(new integer(val)); return result; } ref javascript::makeConstant(unsigned int val) { ref result(new const_integer(val)); return result; } ref javascript::makeValue(signed long val) { ref result(new integer(val)); return result; } ref javascript::makeConstant(signed long val) { ref result(new const_integer(val)); return result; } ref javascript::makeValue(unsigned long val) { ref result(new integer(val)); return result; } ref javascript::makeConstant(unsigned long val) { ref result(new const_integer(val)); return result; } ref javascript::makeValue(double val) { ref result(new floating_point(val)); return result; } ref javascript::makeConstant(double val) { ref result(new const_floating_point(val)); return result; } ref javascript::makeValue(string const &val) { ref result(new js_string(val)); return result; } ref javascript::makeConstant(string const &val) { return wrapConstant(makeValue(val)); } ref javascript::makeArray(TSize size) { auto_ptr result(new js_array(size)); return result.release(); } ref javascript::makeLValue(ref target) { ref result = new lvalue(target); return result; } ref javascript::wrapConstant(ref val) { ref result(new constant_wrapper(val)); return result; }