static ejsval _ejs_RegExp_impl (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { EJSRegExp *re; if (EJSVAL_IS_UNDEFINED(_this)) { // called as a function _this = _ejs_object_new(_ejs_RegExp_prototype, &_ejs_RegExp_specops); } re = (EJSRegExp*)EJSVAL_TO_OBJECT(_this); re->pattern = _ejs_undefined; re->flags = _ejs_undefined; if (argc > 0) re->pattern = args[0]; if (argc > 1) re->flags = args[1]; if (!EJSVAL_IS_STRING(re->pattern)) EJS_NOT_IMPLEMENTED(); EJSPrimString *flat_pattern = _ejs_string_flatten (re->pattern); jschar* chars = flat_pattern->data.flat; const unsigned char* pcre16_tables = pcre16_maketables(); const char *pcre_error; int pcre_erroffset; re->compiled_pattern = pcre16_compile(chars, PCRE_UTF16 | PCRE_NO_UTF16_CHECK, &pcre_error, &pcre_erroffset, pcre16_tables); _ejs_object_define_value_property (_this, _ejs_atom_source, re->pattern, EJS_PROP_NOT_ENUMERABLE | EJS_PROP_NOT_CONFIGURABLE | EJS_PROP_NOT_WRITABLE); if (EJSVAL_IS_STRING(re->flags)) { EJSPrimString *flat_flags = _ejs_string_flatten(re->flags); chars = flat_flags->data.flat; for (int i = 0; i < flat_flags->length; i ++) { if (chars[i] == 'g' && !re->global) { re->global = EJS_TRUE; continue; } else if (chars[i] == 'i' && !re->ignoreCase) { re->ignoreCase = EJS_TRUE; continue; } else if (chars[i] == 'm' && !re->multiline) { re->multiline = EJS_TRUE; continue; } else if (chars[i] == 'y' && !re->sticky) { re->sticky = EJS_TRUE; continue; } else if (chars[i] == 'u' && !re->unicode) { re->unicode = EJS_TRUE; continue; } _ejs_throw_nativeerror_utf8 (EJS_SYNTAX_ERROR, "Invalid flag supplied to RegExp constructor"); } } return _this; }
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); }
// 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_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 EJS_NATIVE_FUNC(_ejs_Process_chdir) { ejsval dir = _ejs_undefined; if (argc > 0) dir = args[0]; if (!EJSVAL_IS_STRING(dir)) _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "chdir passed non-string"); char *dir_utf8 = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(dir)); chdir(dir_utf8); free(dir_utf8); return _ejs_undefined; }
// 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); }
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 ejsval _ejs_require_impl (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { if (argc < 1) { return _ejs_undefined; } ejsval arg = args[0]; if (!EJSVAL_IS_STRING(arg)) { _ejs_log ("required called with non-string\n"); return _ejs_null; } return _ejs_module_get(arg); }
static ejsval _ejs_Process_chdir (ejsval env, ejsval _this, uint32_t argc, ejsval* args) { ejsval dir = _ejs_undefined; if (argc > 0) dir = args[0]; if (!EJSVAL_IS_STRING(dir)) _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "chdir passed non-string"); char *dir_utf8 = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(dir)); chdir(dir_utf8); free(dir_utf8); return _ejs_undefined; }
static ejsval _ejs_path_resolve (ejsval env, ejsval _this, uint32_t argc, ejsval* args) { char** paths_utf8 = (char**)calloc(argc + 1, sizeof(char*)); int num_paths = 0; char cwd[MAXPATHLEN]; getcwd(cwd, MAXPATHLEN); paths_utf8[num_paths++] = strdup(cwd); for (int i = 0; i < argc; i ++) { ejsval arg = args[i]; if (!EJSVAL_IS_STRING(arg)) { for (int j = 0; j < num_paths; j ++) free(paths_utf8[j]); free (paths_utf8); _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "Arguments to path.resolve must be strings"); } paths_utf8[num_paths++] = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(arg)); } int start_path; for (start_path = num_paths-1; start_path >= 0; start_path --) { if (paths_utf8[start_path][0] == '/') break; } // at this point paths_utf8[start_path] is our "root" for // resolving. it is either the right-most absolute path in the // argument list, or $cwd if there wasn't an absolute path in the // args. char* resolved = resolvev(&paths_utf8[start_path], num_paths - start_path); ejsval rv = _ejs_string_new_utf8(resolved); free (resolved); for (int j = 0; j < num_paths; j ++) free(paths_utf8[j]); free (paths_utf8); return rv; }
// ECMA262 15.3.4.5 static ejsval _ejs_Function_prototype_bind (ejsval env, ejsval _this, uint32_t argc, ejsval *args) { /* 1. Let Target be the this value. */ ejsval Target = _this; /* 2. If IsCallable(Target) is false, throw a TypeError exception. */ if (!EJSVAL_IS_CALLABLE(Target)) { _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "object not a function"); } ejsval thisArg = _ejs_undefined; if (argc > 0) thisArg = args[0]; /* 3. Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. */ int bound_argc = argc > 1 ? argc - 1 : 0; /* 4. Let F be a new native ECMAScript object . */ ejsval bound_env = EJS_BOUNDFUNC_ENV_NEW(bound_argc); EJS_BOUNDFUNC_ENV_SET_TARGET(bound_env, Target); EJS_BOUNDFUNC_ENV_SET_THIS(bound_env, thisArg); EJS_BOUNDFUNC_ENV_SET_ARGC(bound_env, NUMBER_TO_EJSVAL(bound_argc)); for (int i = 0; i < bound_argc; i ++) { EJS_BOUNDFUNC_ENV_SET_ARG(bound_env, i, args[i+1]); } ejsval target_name = _ejs_object_getprop (Target, _ejs_atom_name); ejsval bound_name; if (EJSVAL_IS_STRING(target_name)) bound_name = _ejs_string_concat(_ejs_atom_bound_space, target_name); else bound_name = _ejs_atom_bound_space; ejsval F = _ejs_function_new (bound_env, bound_name, bound_wrapper); EJSFunction *F_ = (EJSFunction*)EJSVAL_TO_OBJECT(F); F_->bound = EJS_TRUE; return F; }
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_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; }
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_string(ejsval exp) { return EJSVAL_IS_STRING(exp) ? _ejs_true : _ejs_false; }
static ejsval _ejs_path_relative (ejsval env, ejsval _this, uint32_t argc, ejsval* args) { ejsval from = _ejs_undefined; ejsval to = _ejs_undefined; if (argc > 0) from = args[0]; if (argc > 1) to = args[1]; if (!EJSVAL_IS_STRING(from) || !EJSVAL_IS_STRING(to)) _ejs_throw_nativeerror_utf8 (EJS_TYPE_ERROR, "Arguments to path.relative must be strings"); char *from_utf8 = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(from)); char *to_utf8 = ucs2_to_utf8(EJSVAL_TO_FLAT_STRING(to)); if (from_utf8[0] != '/') from_utf8 = make_absolute(from_utf8); if (to_utf8[0] != '/') to_utf8 = make_absolute(to_utf8); char* p = to_utf8 + strlen(to_utf8) - 1; int up = 0; EJSBool seen_slash = EJS_FALSE; while (p != to_utf8) { if (*p == '/') { if (seen_slash) continue; // skip adjacent slashes seen_slash = EJS_TRUE; char* prefix = strndup(to_utf8, p - to_utf8); if (!strcmp(from_utf8, prefix)) { up = -1; free (prefix); goto done; } if (strstr(from_utf8, prefix) == from_utf8) { free (prefix); goto done; } free (prefix); up ++; } else { seen_slash = EJS_FALSE; } p--; } // we made it all the way to the end, fall through to building up our string done: { ejsval dotdotslash = _ejs_string_new_utf8("../"); ejsval rv = _ejs_string_new_utf8(p+1); while (up >= 0) { rv = _ejs_string_concat(dotdotslash, rv); up--; } free (from_utf8); free (to_utf8); return rv; } }