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); }
ejsval _ejs_typedarray_new_from_array (EJSTypedArrayType element_type, ejsval arrayObj) { EJSObject *arr = EJSVAL_TO_OBJECT(arrayObj); int arrlen = EJSARRAY_LEN(arr); ejsval typedarr = _ejs_typedarray_new (element_type, arrlen); int i; void* data = _ejs_typedarray_get_data (EJSVAL_TO_OBJECT(typedarr)); // this is woefully underoptimized... for (i = 0; i < arrlen; i ++) { ejsval item = _ejs_object_getprop (arrayObj, NUMBER_TO_EJSVAL(i)); switch (element_type) { case EJS_TYPEDARRAY_INT8: ((int8_t*)data)[i] = (int8_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_UINT8: ((uint8_t*)data)[i] = (uint8_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_UINT8CLAMPED: EJS_NOT_IMPLEMENTED(); case EJS_TYPEDARRAY_INT16: ((int16_t*)data)[i] = (int16_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_UINT16: ((uint16_t*)data)[i] = (uint16_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_INT32: ((int32_t*)data)[i] = (int32_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_UINT32: ((uint32_t*)data)[i] = (uint32_t)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_FLOAT32: ((float*)data)[i] = (float)EJSVAL_TO_NUMBER(item); break; case EJS_TYPEDARRAY_FLOAT64: ((double*)data)[i] = (double)EJSVAL_TO_NUMBER(item); break; default: EJS_NOT_REACHED(); } } return typedarr; }
// 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 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_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 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); }
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_DataView_impl (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { if (EJSVAL_IS_UNDEFINED(_this)) { _ejs_log ("DataView called as a function\n"); EJS_NOT_IMPLEMENTED(); } if (argc == 0 || !EJSVAL_IS_ARRAYBUFFER(args[0])) { _ejs_log ("arg0 not an ArrayBuffer object\n"); EJS_NOT_IMPLEMENTED(); } EJSDataView* view = (EJSDataView*)EJSVAL_TO_OBJECT(_this); EJSArrayBuffer* buff = (EJSArrayBuffer*)EJSVAL_TO_OBJECT(args[0]); uint32_t offset; uint32_t len; switch (argc) { case 1: offset = 0; len = buff->size; break; case 2: offset = EJSVAL_TO_NUMBER(args[1]); len = buff->size - offset; break; default: offset = EJSVAL_TO_NUMBER(args[1]); len = EJSVAL_TO_NUMBER(args[2]); } view->buffer = args[0]; view->byteOffset = offset; view->byteLength = len; _ejs_object_define_value_property (_this, _ejs_atom_byteLength, DOUBLE_TO_EJSVAL_IMPL(view->byteLength), EJS_PROP_FLAGS_ENUMERABLE); _ejs_object_define_value_property (_this, _ejs_atom_byteOffset, DOUBLE_TO_EJSVAL_IMPL(view->byteOffset), EJS_PROP_FLAGS_ENUMERABLE); _ejs_object_define_value_property (_this, _ejs_atom_buffer, view->buffer, EJS_PROP_FLAGS_ENUMERABLE); return _this; }
// 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_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_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_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; }
double ToDouble(ejsval exp) { return EJSVAL_TO_NUMBER(ToNumber(exp)); }
// ECMA262 15.3.4.3 static ejsval _ejs_Function_prototype_apply (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { ejsval func = _this; /* 1. If IsCallable(func) is false, then throw a TypeError exception. */ if (!EJSVAL_IS_CALLABLE(_this)) { printf ("throw TypeError, func is not callable\n"); EJS_NOT_IMPLEMENTED(); } ejsval thisArg = _ejs_undefined; ejsval argArray = _ejs_undefined; if (argc > 0) thisArg = args[0]; if (argc > 1) argArray = args[1]; /* 2. If argArray is null or undefined, then */ if (EJSVAL_IS_UNDEFINED(argArray) || EJSVAL_IS_NULL(argArray)) { /* a. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value */ /* and an empty list of arguments. */ return _ejs_invoke_closure (func, thisArg, 0, NULL); } /* 3. If Type(argArray) is not Object, then throw a TypeError exception. */ if (!EJSVAL_IS_OBJECT(argArray)) { printf ("throw TypeError, argArray is not an object\n"); EJS_NOT_IMPLEMENTED(); } EJSObject* argArray_ = EJSVAL_TO_OBJECT(argArray); /* 4. Let len be the result of calling the [[Get]] internal method of argArray with argument "length". */ ejsval len = OP(argArray_,Get) (argArray, _ejs_atom_length, argArray); /* 5. Let n be ToUint32(len). */ uint32_t n = (uint32_t)EJSVAL_TO_NUMBER(len); ejsval* argList; EJSBool argList_allocated = EJS_FALSE; if (EJSVAL_IS_DENSE_ARRAY(argArray)) { argList = EJSDENSEARRAY_ELEMENTS(argArray_); } else { /* 6. Let argList be an empty List. */ argList = (ejsval*)malloc(sizeof(ejsval) * n); argList_allocated = EJS_TRUE; /* 7. Let index be 0. */ int index = 0; /* 8. Repeat while index < n */ while (index < n) { /* a. Let indexName be ToString(index). */ ejsval indexName = NUMBER_TO_EJSVAL(index); /* b. Let nextArg be the result of calling the [[Get]] internal method of argArray with indexName as the */ /* argument. */ ejsval nextArg = OP(argArray_,Get)(argArray, indexName, argArray); /* c. Append nextArg as the last element of argList. */ argList[index] = nextArg; /* d. Set index to index + 1. */ ++index; } } /* 9. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and */ /* argList as the list of arguments. */ ejsval rv = EJSVAL_TO_FUNC(func) (EJSVAL_TO_ENV(func), thisArg, n, argList); if (argList_allocated) free (argList); return rv; }
ejsval _ejs_op_rsh (ejsval lhs, ejsval rhs) { if (EJSVAL_IS_NUMBER(lhs)) { if (EJSVAL_IS_NUMBER(rhs)) { return NUMBER_TO_EJSVAL ((int)((int)EJSVAL_TO_NUMBER(lhs) >> (((unsigned int)EJSVAL_TO_NUMBER(rhs)) & 0x1f))); } else { // need to call valueOf() on the object, or convert the string to a number EJS_NOT_IMPLEMENTED(); } }