static EjsAny *invokeNamespaceOperator(Ejs *ejs, EjsNamespace *lhs, int opCode, EjsNamespace *rhs) { bool boolResult; switch (opCode) { case EJS_OP_COMPARE_EQ: if (!ejsIsDefined(ejs, rhs)) { return ((opCode == EJS_OP_COMPARE_EQ) ? ESV(false): ESV(true)); } boolResult = ejsCompareString(ejs, lhs->value, rhs->value) == 0; break; case EJS_OP_COMPARE_STRICTLY_EQ: boolResult = lhs == rhs; break; case EJS_OP_COMPARE_NE: if (!ejsIsDefined(ejs, rhs)) { return ((opCode == EJS_OP_COMPARE_EQ) ? ESV(false): ESV(true)); } boolResult = !(ejsCompareString(ejs, lhs->value, rhs->value) == 0); break; case EJS_OP_COMPARE_STRICTLY_NE: boolResult = !(lhs == rhs); break; default: ejsThrowTypeError(ejs, "Operation is not valid on this type"); return 0; } return ejsCreateBoolean(ejs, boolResult); }
static EjsString *joinArray(Ejs *ejs, EjsArray *ap, int argc, EjsObj **argv) { EjsString *sep, *sp; MprBuf *buf; ssize len; int i, nonString; sep = (argc == 1) ? (EjsString*) argv[0] : NULL; if (sep == ESV(empty) && ap->length == 1 && ejsIs(ejs, ap->data[0], String)) { /* Optimized path for joining [string]. This happens frequently with fun(...args) */ return (EjsString*) ap->data[0]; } /* Get an estimate of the string length */ len = 0; nonString = 0; for (i = 0; i < ap->length; i++) { sp = (EjsString*) ap->data[i]; if (!ejsIs(ejs, sp, String)) { nonString = 1; continue; } len += sp->length; } if (sep) { len += (ap->length * sep->length); } if (nonString) { len += ME_MAX_BUFFER; } buf = mprCreateBuf(len + 1, -1); for (i = 0; i < ap->length; i++) { sp = (EjsString*) ap->data[i]; if (!ejsIsDefined(ejs, sp)) { continue; } sp = ejsToString(ejs, sp); if (!ejsIsDefined(ejs, sp)) { continue; } if (i > 0 && sep) { mprPutBlockToBuf(buf, sep->value, sep->length); } mprPutBlockToBuf(buf, sp->value, sp->length); } mprAddNullToBuf(buf); return ejsCreateStringFromBytes(ejs, mprGetBufStart(buf), mprGetBufLength(buf)); }
/* Expand a template with {word} tokens from the given options objects function template(pattern: String, ...options): Uri */ static EjsUri *uri_template(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { EjsArray *options; EjsObj *obj, *value; MprBuf *buf; cchar *pattern, *cp, *ep, *str; char *token; int i, len; pattern = ejsToMulti(ejs, argv[0]); options = (EjsArray*) argv[1]; buf = mprCreateBuf(-1, -1); for (cp = pattern; *cp; cp++) { if (*cp == '~' && (cp == pattern || cp[-1] != '\\')) { for (i = 0; i < options->length; i++) { obj = options->data[i]; if ((value = ejsGetPropertyByName(ejs, obj, N(NULL, "scriptName"))) != 0 && ejsIsDefined(ejs, value)) { str = ejsToMulti(ejs, value); if (str && *str) { mprPutStringToBuf(buf, str); break; } else { value = 0; } } } } else if (*cp == '{' && (cp == pattern || cp[-1] != '\\')) { if ((ep = strchr(++cp, '}')) != 0) { len = (int) (ep - cp); token = mprMemdup(cp, len + 1); token[len] = '\0'; value = 0; for (i = 0; i < options->length; i++) { obj = options->data[i]; if ((value = ejsGetPropertyByName(ejs, obj, N(NULL, token))) != 0 && ejsIsDefined(ejs, value)) { str = ejsToMulti(ejs, value); if (str && *str) { mprPutStringToBuf(buf, str); break; } else { value = 0; } } } if (!ejsIsDefined(ejs, value)) { // MOB - remove this. Should not be erasing the prior "/" if (cp >= &pattern[2] && cp[-2] == '/') { mprAdjustBufEnd(buf, -1); } } cp = ep; } } else { mprPutCharToBuf(buf, *cp); } } mprAddNullToBuf(buf); return ejsCreateUriFromAsc(ejs, mprGetBufStart(buf)); }
/* Convert the array to a single string each member of the array has toString called on it and the resulting strings are concatenated. override function toString(): String */ static EjsString *arrayToString(Ejs *ejs, EjsArray *ap, int argc, EjsObj **argv) { EjsString *result, *comma; EjsObj *vp; int i, rc; result = ESV(empty); if (result == 0) { ejsThrowMemoryError(ejs); return 0; } comma = ejsCreateStringFromAsc(ejs, ","); for (i = 0; i < ap->length; i++) { vp = ap->data[i]; rc = 0; if (i > 0) { result = ejsJoinString(ejs, result, comma); } if (ejsIsDefined(ejs, vp)) { result = ejsJoinString(ejs, result, ejsToString(ejs, vp)); } if (rc < 0) { ejsThrowMemoryError(ejs); return 0; } } return result; }
static HttpUri *toHttpUri(Ejs *ejs, EjsObj *arg, int dup) { HttpUri *uri; if (!ejsIsDefined(ejs, arg)) { arg = ESV(empty); } if (ejsIs(ejs, arg, String)) { uri = httpCreateUri(ejsToMulti(ejs, arg), 0); } else if (ejsIs(ejs, arg, Uri)) { if (dup) { uri = httpCloneUri(((EjsUri*) arg)->uri, 0); } else { uri = ((EjsUri*) arg)->uri; } } else if (ejsIs(ejs, arg, Path)) { uri = httpCreateUri(((EjsPath*) arg)->value, 0); } else if (ejsGetLength(ejs, arg) > 0) { uri = createHttpUriFromHash(ejs, arg, 0); } else { arg = (EjsObj*) ejsToString(ejs, arg); uri = httpCreateUri(ejsToMulti(ejs, arg), 0); } return uri; }
/* Constructor function open(options: Object = null): File NOTE: options can be an options hash or as mode string */ static EjsObj *openFile(Ejs *ejs, EjsFile *fp, int argc, EjsObj **argv) { EjsObj *options; cchar *mode; int perms, omode; if (argc < 0 || argc > 1) { ejsThrowArgError(ejs, "Bad args"); return 0; } options = argv[0]; if (argc == 0 || !ejsIsDefined(ejs, options)) { omode = O_RDONLY | O_BINARY; perms = EJS_FILE_PERMS; fp->mode = EJS_FILE_READ; mode = "r"; } else { if (ejsIs(ejs, options, String)) { mode = ejsToMulti(ejs, options); perms = EJS_FILE_PERMS; } else { perms = ejsGetNumOption(ejs, options, "permissions", EJS_FILE_PERMS, 1); mode = getStrOption(ejs, options, "mode", "r", 1); if (ejs->exception) { return 0; } } omode = mapMode(mode); if (!(omode & O_WRONLY)) { fp->mode |= EJS_FILE_READ; } if (omode & (O_WRONLY | O_RDWR)) { fp->mode |= EJS_FILE_WRITE; } } fp->modeString = sclone(mode); fp->perms = perms; if (fp->file) { mprCloseFile(fp->file); } fp->file = mprOpenFile(fp->path, omode, perms); if (fp->file == 0) { ejsThrowIOError(ejs, "Cannot open %s", fp->path); return 0; } if (options) { ejsSetPathAttributes(ejs, fp->path, options); } #if ME_CC_MMU && FUTURE mprGetPathInfo(&fp->info); fp->mapped = mapFile(fp, fp->info.size, MPR_MAP_READ | MPR_MAP_WRITE); #endif fp->mode |= EJS_FILE_OPEN; return (EjsObj*) fp; }
/* native function XML(value: Object = null) */ static EjsObj *xmlConstructor(Ejs *ejs, EjsXML *thisObj, int argc, EjsObj **argv) { EjsObj *arg, *vp; wchar *str; // TODO - should be also able to handle a File object if (thisObj == 0) { /* Called as a function - cast the arg */ if (argc > 0) { if ((arg = ejsCast(ejs, argv[0], String)) == 0) { return 0; } } thisObj = ejsCreateXML(ejs, 0, N(NULL, NULL), NULL, NULL); } if (argc == 0) { return (EjsObj*) thisObj; } arg = argv[0]; assert(arg); if (!ejsIsDefined(ejs, arg)) { return (EjsObj*) thisObj; } arg = ejsCast(ejs, argv[0], String); if (arg && ejsIs(ejs, arg, String)) { str = ((EjsString*) arg)->value; if (str == 0) { return 0; } while (isspace((uchar) *str)) str++; if (*str == '<') { /* XML Literal */ ejsLoadXMLString(ejs, thisObj, (EjsString*) arg); } else { /* Load from file */ loadXml(ejs, thisObj, argc, argv); } } else if (arg && ejsIsXML(ejs, arg)) { if ((vp = xmlToString(ejs, argv[0], 0, NULL)) != 0) { ejsLoadXMLString(ejs, thisObj, (EjsString*) vp); } } else { ejsThrowArgError(ejs, "Bad type passed to XML constructor"); return 0; } return (EjsObj*) thisObj; }
/* Cast operands as required for invokeArrayOperator */ static EjsObj *coerceArrayOperands(Ejs *ejs, EjsObj *lhs, int opcode, EjsObj *rhs) { switch (opcode) { /* Binary operators */ case EJS_OP_ADD: return ejsInvokeOperator(ejs, arrayToString(ejs, (EjsArray*) lhs, 0, 0), opcode, rhs); case EJS_OP_AND: case EJS_OP_DIV: case EJS_OP_MUL: case EJS_OP_OR: case EJS_OP_REM: case EJS_OP_SHL: case EJS_OP_SHR: case EJS_OP_SUB: case EJS_OP_USHR: case EJS_OP_XOR: return ejsInvokeOperator(ejs, ESV(zero), opcode, rhs); case EJS_OP_COMPARE_EQ: case EJS_OP_COMPARE_NE: if (!ejsIsDefined(ejs, rhs)) { return ((opcode == EJS_OP_COMPARE_EQ) ? ESV(false): ESV(true)); } else if (ejsIs(ejs, rhs, Number)) { return ejsInvokeOperator(ejs, ejsToNumber(ejs, lhs), opcode, rhs); } return ejsInvokeOperator(ejs, ejsToString(ejs, lhs), opcode, rhs); case EJS_OP_COMPARE_LE: case EJS_OP_COMPARE_LT: case EJS_OP_COMPARE_GE: case EJS_OP_COMPARE_GT: if (ejsIs(ejs, rhs, Number)) { return ejsInvokeOperator(ejs, ejsToNumber(ejs, lhs), opcode, rhs); } return ejsInvokeOperator(ejs, ejsToString(ejs, lhs), opcode, rhs); case EJS_OP_COMPARE_STRICTLY_NE: case EJS_OP_COMPARE_UNDEFINED: case EJS_OP_COMPARE_NOT_ZERO: case EJS_OP_COMPARE_NULL: return ESV(true); case EJS_OP_COMPARE_STRICTLY_EQ: case EJS_OP_COMPARE_FALSE: case EJS_OP_COMPARE_TRUE: case EJS_OP_COMPARE_ZERO: return ESV(false); /* Unary operators */ case EJS_OP_LOGICAL_NOT: case EJS_OP_NOT: case EJS_OP_NEG: return 0; default: ejsThrowTypeError(ejs, "Opcode %d not valid for type %@", opcode, TYPE(lhs)->qname.name); return ESV(undefined); } }
static EjsAny *coerceUriOperands(Ejs *ejs, EjsUri *lhs, int opcode, EjsAny *rhs) { HttpUri *uri; char *ustr; switch (opcode) { /* Binary operators */ case EJS_OP_ADD: uri = lhs->uri; ustr = httpFormatUri(uri->scheme, uri->host, uri->port, uri->path, uri->reference, uri->query, 0); return ejsInvokeOperator(ejs, ejsCreateStringFromAsc(ejs, ustr), opcode, rhs); case EJS_OP_COMPARE_EQ: case EJS_OP_COMPARE_NE: case EJS_OP_COMPARE_LE: case EJS_OP_COMPARE_LT: case EJS_OP_COMPARE_GE: case EJS_OP_COMPARE_GT: if (!ejsIsDefined(ejs, rhs)) { return ((opcode == EJS_OP_COMPARE_EQ) ? ESV(false): ESV(true)); } uri = lhs->uri; ustr = httpFormatUri(uri->scheme, uri->host, uri->port, uri->path, uri->reference, uri->query, 0); return ejsInvokeOperator(ejs, ejsCreateStringFromAsc(ejs, ustr), opcode, rhs); case EJS_OP_COMPARE_STRICTLY_NE: return ESV(true); case EJS_OP_COMPARE_STRICTLY_EQ: return ESV(false); case EJS_OP_COMPARE_NOT_ZERO: case EJS_OP_COMPARE_TRUE: return ESV(true); case EJS_OP_COMPARE_ZERO: case EJS_OP_COMPARE_FALSE: return ESV(false); case EJS_OP_COMPARE_UNDEFINED: case EJS_OP_COMPARE_NULL: return ESV(false); default: ejsThrowTypeError(ejs, "Opcode %d not valid for type %@", opcode, TYPE(lhs)->qname.name); return ESV(undefined); } return 0; }
/* Compact an array. Remove all null elements. function compact() : Array */ static EjsArray *compactArray(Ejs *ejs, EjsArray *ap, int argc, EjsObj **argv) { EjsObj **data, **src, **dest; int i, oldLen; data = ap->data; src = dest = &data[0]; for (i = 0; i < ap->length; i++, src++) { if (*src == 0 || !ejsIsDefined(ejs, *src)) { continue; } *dest++ = *src; } oldLen = ap->length; ap->length = (int) (dest - &data[0]); for (i = ap->length; i < oldLen; i++) { *dest++ = ESV(undefined); } return ap; }
static EjsUri *completeUri(Ejs *ejs, EjsUri *up, EjsObj *missing, int includeQuery) { EjsUri *missingUri; if (!ejsIsDefined(ejs, missing)) { missingUri = 0; } else if (ejsGetLength(ejs, missing) > 0) { missingUri = ejsCreateObj(ejs, ESV(Uri), 0); missingUri->uri = createHttpUriFromHash(ejs, missing, HTTP_COMPLETE_URI); } else { missingUri = ejsToUri(ejs, missing); } if (missingUri == 0) { if (!includeQuery) { up->uri->query = NULL; } httpCompleteUri(up->uri, NULL); } else { httpCompleteUri(up->uri, missingUri->uri); } return up; }
/* UNUSED */ static EjsString *joinArray(Ejs *ejs, EjsArray *ap, int argc, EjsObj **argv) { EjsString *result, *sep; EjsObj *vp; int i; sep = (argc == 1) ? (EjsString*) argv[0] : NULL; if (sep == ESV(empty) && ap->length == 1 && ejsIs(ejs, ap->data[0], String)) { /* Optimized path for joining [string]. This happens frequently with fun(...args) */ return (EjsString*) ap->data[0]; } result = ESV(empty); for (i = 0; i < ap->length; i++) { vp = ap->data[i]; if (!ejsIsDefined(ejs, vp)) { continue; } if (i > 0 && sep) { result = ejsJoinString(ejs, result, sep); } result = ejsJoinString(ejs, result, ejsToString(ejs, vp)); } return result; }
static EjsString *serialize(Ejs *ejs, EjsAny *vp, Json *json) { EjsName qname; EjsFunction *fn; EjsString *result, *sv; EjsTrait *trait; EjsObj *pp, *obj, *replacerArgs[2]; wchar *cp; cchar *key; int c, isArray, i, count, slotNum, quotes; /* The main code below can handle Arrays, Objects, objects derrived from Object and also native classes with properties. All others just use toString. */ count = ejsIsPot(ejs, vp) ? ejsGetLength(ejs, vp) : 0; if (count == 0 && TYPE(vp) != ESV(Object) && TYPE(vp) != ESV(Array)) { // OPT - need some flag for this test. if (!ejsIsDefined(ejs, vp) || ejsIs(ejs, vp, Boolean) || ejsIs(ejs, vp, Number)) { return ejsToString(ejs, vp); } else if (json->regexp) { return ejsToString(ejs, vp); } else { return ejsToLiteralString(ejs, vp); } } obj = vp; json->nest++; if (json->buf == 0) { json->buf = mprCreateBuf(0, 0); mprAddRoot(json->buf); } isArray = ejsIs(ejs, vp, Array); mprPutCharToWideBuf(json->buf, isArray ? '[' : '{'); if (json->pretty) { mprPutCharToWideBuf(json->buf, '\n'); } if (++ejs->serializeDepth <= json->depth && !VISITED(obj)) { SET_VISITED(obj, 1); for (slotNum = 0; slotNum < count && !ejs->exception; slotNum++) { trait = ejsGetPropertyTraits(ejs, obj, slotNum); if (trait && (trait->attributes & (EJS_TRAIT_HIDDEN | EJS_TRAIT_DELETED | EJS_FUN_INITIALIZER | EJS_FUN_MODULE_INITIALIZER)) && !json->hidden) { continue; } pp = ejsGetProperty(ejs, obj, slotNum); if (ejs->exception) { SET_VISITED(obj, 0); json->nest--; return 0; } if (pp == 0) { continue; } if (isArray) { key = itos(slotNum); qname.name = ejsCreateStringFromAsc(ejs, key); qname.space = ESV(empty); } else { qname = ejsGetPropertyName(ejs, vp, slotNum); } quotes = json->quotes; if (!quotes) { // UNICODE for (cp = qname.name->value; cp < &qname.name->value[qname.name->length]; cp++) { if (!isalnum((uchar) *cp) && *cp != '_') { quotes = 1; break; } } } if (json->pretty) { for (i = 0; i < ejs->serializeDepth; i++) { mprPutStringToWideBuf(json->buf, json->indent); } } if (!isArray) { if (json->namespaces) { if (qname.space != ESV(empty)) { mprPutToBuf(json->buf, "\"%@\"::", qname.space); } } if (quotes) { mprPutCharToWideBuf(json->buf, '"'); } for (cp = qname.name->value; cp && *cp; cp++) { c = *cp; if (c == '"' || c == '\\') { mprPutCharToWideBuf(json->buf, '\\'); mprPutCharToWideBuf(json->buf, c); } else { mprPutCharToWideBuf(json->buf, c); } } if (quotes) { mprPutCharToWideBuf(json->buf, '"'); } mprPutCharToWideBuf(json->buf, ':'); if (json->pretty) { mprPutCharToWideBuf(json->buf, ' '); } } fn = (EjsFunction*) ejsGetPropertyByName(ejs, TYPE(pp)->prototype, N(NULL, "toJSON")); // OPT - check that this is going directly to serialize most of the time if (!ejsIsFunction(ejs, fn) || (fn->isNativeProc && fn->body.proc == (EjsProc) ejsObjToJSON)) { sv = serialize(ejs, pp, json); } else { sv = (EjsString*) ejsRunFunction(ejs, fn, pp, 1, &json->options); } if (sv == 0 || !ejsIs(ejs, sv, String)) { if (ejs->exception) { ejsThrowTypeError(ejs, "Cannot serialize property %@", qname.name); SET_VISITED(obj, 0); return 0; } } else { if (json->replacer) { replacerArgs[0] = (EjsObj*) qname.name; replacerArgs[1] = (EjsObj*) sv; /* function replacer(key: String, value: String): String */ sv = ejsRunFunction(ejs, json->replacer, obj, 2, (EjsObj**) replacerArgs); } mprPutBlockToBuf(json->buf, sv->value, sv->length * sizeof(wchar)); } if ((slotNum + 1) < count || json->commas) { mprPutCharToWideBuf(json->buf, ','); } if (json->pretty) { mprPutCharToWideBuf(json->buf, '\n'); } } SET_VISITED(obj, 0); } --ejs->serializeDepth; if (json->pretty) { for (i = ejs->serializeDepth; i > 0; i--) { mprPutStringToWideBuf(json->buf, json->indent); } } mprPutCharToWideBuf(json->buf, isArray ? ']' : '}'); mprAddNullToWideBuf(json->buf); if (--json->nest == 0) { result = ejsCreateString(ejs, mprGetBufStart(json->buf), mprGetBufLength(json->buf) / sizeof(wchar)); mprRemoveRoot(json->buf); } else { result = 0; } return result; }