static void _ejs_dataview_specop_put (ejsval obj, ejsval propertyName, ejsval val, ejsval receiver, EJSBool flag) { EJSBool is_index = EJS_FALSE; ejsval idx_val = ToNumber(propertyName); int idx; if (EJSVAL_IS_NUMBER(idx_val)) { double n = EJSVAL_TO_NUMBER(idx_val); if (floor(n) == n) { idx = (int)n; is_index = EJS_TRUE; } } if (is_index) { if (idx < 0 || idx >= EJS_DATA_VIEW_BYTE_LEN(obj)) return; void* data = _ejs_dataview_get_data (EJSVAL_TO_OBJECT(obj)); ((unsigned char*)data)[idx] = (unsigned char)EJSVAL_TO_NUMBER(val); return; } _ejs_Object_specops.put (obj, propertyName, val, receiver, flag); }
// returns an EJSPrimString*. // maybe we could change it to return a char* to match ToDouble? that way string concat wouldn't create // temporary strings for non-PrimString objects only to throw them away after concatenation? ejsval ToString(ejsval exp) { if (EJSVAL_IS_MAGIC_IMPL(exp)) { // holes in dense arrays end up here return _ejs_atom_empty; } else if (EJSVAL_IS_NULL(exp)) return _ejs_atom_null; else if (EJSVAL_IS_UNDEFINED(exp)) return _ejs_atom_undefined; else if (EJSVAL_IS_BOOLEAN(exp)) return EJSVAL_TO_BOOLEAN(exp) ? _ejs_atom_true : _ejs_atom_false; else if (EJSVAL_IS_NUMBER(exp)) return NumberToString(EJSVAL_TO_NUMBER(exp)); else if (EJSVAL_IS_STRING(exp)) return exp; else if (EJSVAL_IS_OBJECT(exp)) { ejsval toString = _ejs_object_getprop (exp, _ejs_atom_toString); if (!EJSVAL_IS_FUNCTION(toString)) { return _ejs_Object_prototype_toString(_ejs_null, exp, 0, NULL); } // should we be checking if this returns a string? i'd assume so... return _ejs_invoke_closure (toString, exp, 0, NULL); } else EJS_NOT_IMPLEMENTED(); }
static ejsval _ejs_dataview_specop_get (ejsval obj, ejsval propertyName, ejsval receiver) { // check if propertyName is an integer, or a string that we can convert to an int EJSBool is_index = EJS_FALSE; int idx = 0; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; is_index = EJS_TRUE; } } // Index for DataView is byte-based. if (is_index) { if (idx < 0 || idx > EJS_DATA_VIEW_BYTE_LEN(obj)) return _ejs_undefined; void *data = _ejs_dataview_get_data (EJSVAL_TO_OBJECT(obj)); return NUMBER_TO_EJSVAL ((double)((unsigned char*)data)[idx]); } // otherwise we fallback to the object implementation return _ejs_Object_specops.get (obj, propertyName, receiver); }
static ejsval _ejs_arguments_specop_get (ejsval obj, ejsval propertyName, ejsval receiver) { EJSArguments* arguments = EJSVAL_TO_ARGUMENTS(obj); // check if propertyName is an integer, or a string that we can convert to an int EJSBool is_index = EJS_FALSE; ejsval idx_val = ToNumber(propertyName); int idx; if (EJSVAL_IS_NUMBER(idx_val)) { double n = EJSVAL_TO_NUMBER(idx_val); if (floor(n) == n) { idx = (int)n; is_index = EJS_TRUE; } } if (is_index) { if (idx < 0 || idx > arguments->argc) { printf ("getprop(%d) on an arguments, returning undefined\n", idx); return _ejs_undefined; } return arguments->args[idx]; } // we also handle the length getter here if (EJSVAL_IS_STRING(propertyName) && !ucs2_strcmp (_ejs_ucs2_length, EJSVAL_TO_FLAT_STRING(propertyName))) { return NUMBER_TO_EJSVAL(arguments->argc); } // otherwise we fallback to the object implementation return _ejs_Object_specops.Get (obj, propertyName, receiver); }
static void _ejs_arraybuffer_specop_put (ejsval obj, ejsval propertyName, ejsval val, ejsval receiver, EJSBool flag) { // check if propertyName is a uint32, or a string that we can convert to an uint32 int idx = -1; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; } } if (idx != -1) { if (idx >= EJS_DENSE_ARRAY_ALLOC(obj)) { int new_alloc = idx + 10; ejsval* new_elements = (ejsval*)malloc (sizeof(ejsval) * new_alloc); memmove (new_elements, EJS_DENSE_ARRAY_ELEMENTS(obj), EJS_DENSE_ARRAY_ALLOC(obj) * sizeof(ejsval)); free (EJS_DENSE_ARRAY_ELEMENTS(obj)); EJS_DENSE_ARRAY_ELEMENTS(obj) = new_elements; EJS_DENSE_ARRAY_ALLOC(obj) = new_alloc; } EJS_DENSE_ARRAY_ELEMENTS(obj)[idx] = val; EJS_ARRAY_LEN(obj) = idx + 1; if (EJS_ARRAY_LEN(obj) >= EJS_DENSE_ARRAY_ALLOC(obj)) abort(); return; } // if we fail there, we fall back to the object impl below _ejs_Object_specops.put (obj, propertyName, val, receiver, flag); }
static ejsval _ejs_arraybuffer_specop_get (ejsval obj, ejsval propertyName, ejsval receiver) { // check if propertyName is an integer, or a string that we can convert to an int EJSBool is_index = EJS_FALSE; int idx = 0; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; is_index = EJS_TRUE; } } if (is_index) { if (idx < 0 || idx > EJS_ARRAY_LEN(obj)) { printf ("getprop(%d) on an array, returning undefined\n", idx); return _ejs_undefined; } return EJS_DENSE_ARRAY_ELEMENTS(obj)[idx]; } // we also handle the length getter here if (EJSVAL_IS_STRING(propertyName) && !ucs2_strcmp (_ejs_ucs2_byteLength, EJSVAL_TO_FLAT_STRING(propertyName))) { return NUMBER_TO_EJSVAL (EJS_ARRAY_BUFFER_BYTE_LEN(obj)); } // otherwise we fallback to the object implementation return _ejs_Object_specops.get (obj, propertyName, receiver); }
// ES6: 23.2.3.1 Set.prototype.add ( value ) ejsval _ejs_Set_prototype_add (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { ejsval value = _ejs_undefined; if (argc > 0) value = args[0]; // 1. Let S be the this value. ejsval S = _this; // 2. If Type(S) is not Object, then throw a TypeError exception. if (!EJSVAL_IS_OBJECT(S)) { _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "Set.prototype.add called with non-object this."); } // 3. If S does not have a [[SetData]] internal slot throw a TypeError exception. if (!EJSVAL_IS_SET(S)) _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "Set.prototype.set called with non-Set this."); // 4. If S’s [[SetData]] internal slot is undefined, then throw a TypeError exception. EJSSet* _set = EJSVAL_TO_SET(S); // 5. Let entries be the List that is the value of S’s [[SetData]] internal slot. EJSSetValueEntry* entries = _set->head_insert; EJSSetValueEntry* e; // 6. Repeat for each e that is an element of entries, for (e = entries; e; e = e->next_insert) { // a. If e is not empty and SameValueZero(e, value) is true, then if (SameValueZero(e->value, value)) // i. Return S. return S; } // 7. If value is −0, then let value be +0. if (EJSVAL_IS_NUMBER(value) && EJSDOUBLE_IS_NEGZERO(EJSVAL_TO_NUMBER(value))) value = NUMBER_TO_EJSVAL(0); // 8. Append value as the last element of entries. e = calloc (1, sizeof (EJSSetValueEntry)); e->value = value; if (!_set->head_insert) _set->head_insert = e; if (_set->tail_insert) { _set->tail_insert->next_insert = e; _set->tail_insert = e; } else { _set->tail_insert = e; } // 9. Return S. return S; }
// same as SameValue, except in its treatment of +/- 0 EJSBool SameValueZero(ejsval x, ejsval y) { // 1. ReturnIfAbrupt(x). // 2. ReturnIfAbrupt(y). // 3. If Type(x) is different from Type(y), return false. if (EJSVAL_TO_TAG(x) != EJSVAL_TO_TAG(y)) return EJS_FALSE; // 4. If Type(x) is Undefined, return true. if (EJSVAL_IS_UNDEFINED(x)) return EJS_TRUE; // 5. If Type(x) is Null, return true. if (EJSVAL_IS_NULL(x)) return EJS_TRUE; // 6. If Type(x) is Number, then if (EJSVAL_IS_NUMBER(x)) { // a. If x is NaN and y is NaN, return true. if (isnan(EJSVAL_TO_NUMBER(x)) && isnan(EJSVAL_TO_NUMBER(y))) return EJS_TRUE; // b. If x is +0 and y is -0, return true. if (EJSVAL_TO_NUMBER(x) == 0.0 && EJSDOUBLE_IS_NEGZERO(EJSVAL_TO_NUMBER(y))) return EJS_TRUE; // c. If x is -0 and y is +0, return tryue. if (EJSDOUBLE_IS_NEGZERO(EJSVAL_TO_NUMBER(x)) == 0.0 && EJSVAL_TO_NUMBER(y) == 0) return EJS_TRUE; // d. If x is the same Number value as y, return true. if (EJSVAL_TO_NUMBER(x) == EJSVAL_TO_NUMBER(y)) return EJS_TRUE; // e. Return false. return EJS_FALSE; } // 7. If Type(x) is String, then if (EJSVAL_IS_STRING(x)) { // a. If x and y are exactly the same sequence of code units (same length and same code units in corresponding positions) return true; // otherwise, return false. if (EJSVAL_TO_STRLEN(x) != EJSVAL_TO_STRLEN(y)) return EJS_FALSE; // XXX there is doubtless a more efficient way to compare two ropes, but we convert but to flat strings for now. return ucs2_strcmp (EJSVAL_TO_FLAT_STRING(x), EJSVAL_TO_FLAT_STRING(y)) ? EJS_FALSE : EJS_TRUE; } // 8. If Type(x) is Boolean, then if (EJSVAL_IS_BOOLEAN(x)) { // a. If x and y are both true or both false, then return true; otherwise, return false. return EJSVAL_TO_BOOLEAN(x) == EJSVAL_TO_BOOLEAN(y) ? EJS_TRUE : EJS_FALSE; } // 9. If Type(x) is Symbol, then if (EJSVAL_IS_SYMBOL(x)) { // a. If x and y are both the same Symbol value, then return true; otherwise, return false. EJS_NOT_IMPLEMENTED(); } // 10. Return true if x and y are the same Object value. Otherwise, return false. return EJSVAL_EQ(x, y); }
ejsval _ejs_op_mod (ejsval lhs, ejsval rhs) { if (EJSVAL_IS_NUMBER(lhs)) { if (EJSVAL_IS_NUMBER(rhs)) { return NUMBER_TO_EJSVAL (fmod(EJSVAL_TO_NUMBER(lhs), EJSVAL_TO_NUMBER(rhs))); } else { // need to call valueOf() on the object, or convert the string to a number EJS_NOT_IMPLEMENTED(); } } else if (EJSVAL_IS_STRING(lhs)) { // string+ with anything we don't implement yet - it will call toString() on objects, and convert a number to a string EJS_NOT_IMPLEMENTED(); } else { // object+... how does js implement this anyway? EJS_NOT_IMPLEMENTED(); } return _ejs_nan; }
static EJSPropertyDesc* _ejs_dataview_specop_get_own_property (ejsval obj, ejsval propertyName) { if (EJSVAL_IS_NUMBER(propertyName)) { double needle = EJSVAL_TO_NUMBER(propertyName); int needle_int; if (EJSDOUBLE_IS_INT32(needle, &needle_int)) { if (needle_int >= 0 && needle_int < EJS_DATA_VIEW_BYTE_LEN(obj)) return NULL; // XXX } } return _ejs_Object_specops.get_own_property (obj, propertyName); }
EJSBool ToEJSBool(ejsval exp) { if (EJSVAL_IS_NULL(exp) || EJSVAL_IS_UNDEFINED(exp)) return EJS_FALSE; else if (EJSVAL_IS_BOOLEAN(exp)) return EJSVAL_TO_BOOLEAN(exp); else if (EJSVAL_IS_NUMBER(exp)) return EJSVAL_TO_NUMBER(exp) != 0; else if (EJSVAL_IS_STRING(exp)) return EJSVAL_TO_STRLEN(exp) != 0; else if (EJSVAL_IS_OBJECT(exp)) return EJS_TRUE; else EJS_NOT_IMPLEMENTED(); }
static EJSPropertyDesc* _ejs_arraybuffer_specop_get_own_property (ejsval obj, ejsval propertyName) { if (EJSVAL_IS_NUMBER(propertyName)) { double needle = EJSVAL_TO_NUMBER(propertyName); int needle_int; if (EJSDOUBLE_IS_INT32(needle, &needle_int)) { if (needle_int >= 0 && needle_int < EJS_ARRAY_LEN(obj)) return NULL; // XXX } } // XXX we need to handle the length property here (see EJSArray's get_own_property) return _ejs_Object_specops.get_own_property (obj, propertyName); }
static EJSBool _ejs_dataview_specop_has_property (ejsval obj, ejsval propertyName) { // check if propertyName is a uint32, or a string that we can convert to an uint32 int idx = -1; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; return idx > 0 && idx < EJS_DATA_VIEW_BYTE_LEN(obj); } } return _ejs_Object_specops.has_property (obj, propertyName); }
static ejsval Constant_getIntegerValue (ejsval env, ejsval _this, int argc, ejsval *args) { REQ_LLVM_TYPE_ARG (0, ty); REQ_INT_ARG (1, v); if (argc == 2) { return Value_new (llvm::Constant::getIntegerValue(ty, llvm::APInt(ty->getPrimitiveSizeInBits(), v))); } else if (argc == 3 && EJSVAL_IS_NUMBER(args[2]) && ty->getPrimitiveSizeInBits() == 64) { uint64_t vhi = v; uint32_t vlo = (uint32_t)EJSVAL_TO_NUMBER(args[2]); return Value_new (llvm::Constant::getIntegerValue(ty, llvm::APInt(ty->getPrimitiveSizeInBits(), (int64_t)((vhi << 32) | vlo)))); } else abort(); // FIXME throw an exception }
static EJSBool _ejs_arguments_specop_has_property (ejsval obj, ejsval propertyName) { EJSArguments* arguments = (EJSArguments*)EJSVAL_TO_OBJECT(obj); // check if propertyName is an integer, or a string that we can convert to an int ejsval idx_val = ToNumber(propertyName); int idx; if (EJSVAL_IS_NUMBER(idx_val)) { double n = EJSVAL_TO_NUMBER(idx_val); if (floor(n) == n) { idx = (int)n; return idx >= 0 && idx < arguments->argc; } } // if we fail there, we fall back to the object impl below return _ejs_Object_specops.HasProperty (obj, propertyName); }
static EJSBool _ejs_arraybuffer_specop_has_property (ejsval obj, ejsval propertyName) { // check if propertyName is a uint32, or a string that we can convert to an uint32 int idx = -1; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; return idx > 0 && idx < EJS_ARRAY_LEN(obj); } } // if we fail there, we fall back to the object impl below return _ejs_Object_specops.has_property (obj, propertyName); }
static EJSBool _ejs_arraybuffer_specop_delete (ejsval obj, ejsval propertyName, EJSBool flag) { // check if propertyName is a uint32, or a string that we can convert to an uint32 int idx = -1; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; } } if (idx == -1) return _ejs_Object_specops._delete (obj, propertyName, flag); // if it's outside the array bounds, do nothing if (idx < EJS_ARRAY_LEN(obj)) EJS_DENSE_ARRAY_ELEMENTS(obj)[idx] = _ejs_undefined; return EJS_TRUE; }
static EJSBool _ejs_dataview_specop_delete (ejsval obj, ejsval propertyName, EJSBool flag) { int idx = -1; if (EJSVAL_IS_NUMBER(propertyName)) { double n = EJSVAL_TO_NUMBER(propertyName); if (floor(n) == n) { idx = (int)n; } } if (idx == -1) return _ejs_Object_specops._delete (obj, propertyName, flag); if (idx < EJS_DATA_VIEW_BYTE_LEN(obj)) { //void* data = _ejs_dataview_get_data (EJSVAL_TO_OBJECT(obj)); //((unsigned char*)data)[idx] = _ejs_undefined; } return EJS_FALSE; }
ejsval ToNumber(ejsval exp) { if (EJSVAL_IS_NUMBER(exp)) return exp; else if (EJSVAL_IS_BOOLEAN(exp)) return EJSVAL_TO_BOOLEAN(exp) ? _ejs_one : _ejs_zero; else if (EJSVAL_IS_STRING(exp)) { char* num_utf8 = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(exp)); char *endptr; double d = strtod(num_utf8, &endptr); if (*endptr != '\0') return _ejs_nan; ejsval rv = NUMBER_TO_EJSVAL(d); // XXX NaN free (num_utf8); return rv; } else if (EJSVAL_IS_UNDEFINED(exp)) return _ejs_nan; else if (EJSVAL_IS_OBJECT(exp)) { if (EJSVAL_IS_DATE(exp)) { return NUMBER_TO_EJSVAL(_ejs_date_get_time ((EJSDate*)EJSVAL_TO_OBJECT(exp))); } else if (EJSVAL_IS_ARRAY(exp)) { int len = EJS_ARRAY_LEN(exp); if (len == 0) return _ejs_zero; else if (len > 1) return _ejs_nan; else { // XXX we need to support sparse arrays here too EJS_ASSERT (EJSVAL_IS_DENSE_ARRAY(exp)); return ToNumber(EJS_DENSE_ARRAY_ELEMENTS(exp)[0]); } } else return _ejs_nan; } else EJS_NOT_IMPLEMENTED(); }
ejsval _ejs_op_typeof (ejsval exp) { if (EJSVAL_IS_NULL(exp)) return _ejs_atom_null; else if (EJSVAL_IS_BOOLEAN(exp)) return _ejs_atom_boolean; else if (EJSVAL_IS_STRING(exp)) return _ejs_atom_string; else if (EJSVAL_IS_SYMBOL(exp)) return _ejs_atom_symbol; else if (EJSVAL_IS_NUMBER(exp)) return _ejs_atom_number; else if (EJSVAL_IS_UNDEFINED(exp)) return _ejs_atom_undefined; else if (EJSVAL_IS_OBJECT(exp)) { if (EJSVAL_IS_FUNCTION(exp)) return _ejs_atom_function; else return _ejs_atom_object; } else EJS_NOT_IMPLEMENTED(); }
ejsval ToObject(ejsval exp) { if (EJSVAL_IS_BOOLEAN(exp)) { ejsval new_boolean = _ejs_object_new (_ejs_Boolean_proto, &_ejs_Boolean_specops); _ejs_invoke_closure (_ejs_Boolean, new_boolean, 1, &exp); return new_boolean; } else if (EJSVAL_IS_NUMBER(exp)) { ejsval new_number = _ejs_object_new (_ejs_Number_proto, &_ejs_Number_specops); _ejs_invoke_closure (_ejs_Number, new_number, 1, &exp); return new_number; } else if (EJSVAL_IS_STRING(exp)) { ejsval new_str = _ejs_object_new (_ejs_String_prototype, &_ejs_String_specops); _ejs_invoke_closure (_ejs_String, new_str, 1, &exp); return new_str; } else if (EJSVAL_IS_UNDEFINED(exp)) return exp; // XXX else if (EJSVAL_IS_OBJECT(exp)) return exp; else EJS_NOT_IMPLEMENTED(); }
ejsval _ejs_op_typeof_is_number(ejsval exp) { return EJSVAL_IS_NUMBER(exp) ? _ejs_true : _ejs_false; }