/* * 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 EjsVar *arrayToString(Ejs *ejs, EjsArray *ap, int argc, EjsVar **argv) { EjsString *result; EjsVar *vp; int i, rc; result = ejsCreateString(ejs, ""); if (result == 0) { ejsThrowMemoryError(ejs); return 0; } for (i = 0; i < ap->length; i++) { vp = ap->data[i]; rc = 0; if (i > 0) { rc = ejsStrcat(ejs, result, (EjsVar*) ejsCreateString(ejs, ",")); } if (vp != 0 && vp != ejs->undefinedValue && vp != ejs->nullValue) { rc = ejsStrcat(ejs, result, vp); } if (rc < 0) { ejsThrowMemoryError(ejs); return 0; } } return (EjsVar*) result; }
/** Get a serialized string representation of a variable using JSON encoding. @return Returns a string variable or null if an exception is thrown. */ EjsString *ejsToJson(Ejs *ejs, EjsVar *vp) { EjsFunction *fn; EjsString *result; if (vp == 0) { return ejsCreateString(ejs, "undefined"); } if (vp->jsonVisited) { return ejsCreateString(ejs, "this"); } vp->jsonVisited = 1; /* * Types can provide a toJSON method, a serializeVar helper. If neither are provided, toString is used as a fall-back. */ fn = (EjsFunction*) ejsGetProperty(ejs, (EjsVar*) vp->type, ES_Object_toJSON); if (ejsIsFunction(fn)) { result = (EjsString*) ejsRunFunction(ejs, fn, vp, 0, NULL); } else { result = ejsToString(ejs, vp); } vp->jsonVisited = 0; return result; }
/* * DB Constructor and also used for constructor for sub classes. * * function DB(connectionString: String) */ static EjsVar *dbConstructor(Ejs *ejs, EjsDb *db, int argc, EjsVar **argv) { sqlite3 *sdb; EjsDb **dbp; char *path; path = ejsGetString(argv[0]); /* * Create a memory context for use by sqlite. This is a virtual paged memory region. * TODO - this is not ideal for long running applications. */ db->arena = mprAllocArena(ejs, "sqlite", EJS_MAX_DB_MEM, 0, 0); if (db->arena == 0) { return 0; } /* * Create a destructor object so we can cleanup and close the database. Must create after the arena so it will be * invoked before the arena is freed. */ if ((dbp = mprAllocObject(ejs, 1, (MprDestructor) dbDestructor)) == 0) { ejsThrowMemoryError(ejs); return 0; } *dbp = db; db->tls = mprCreateThreadLocal(db->arena); if (db->tls == 0) { return 0; } ejsSetDbMemoryContext(db->tls, db->arena); sdb = 0; if (sqlite3_open(path, &sdb /* TODO remove , SQLITE_OPEN_READWRITE, 0 */) != SQLITE_OK) { ejsThrowIOError(ejs, "Can't open database %s", path); return 0; } db->sdb = sdb; sqlite3_busy_timeout(sdb, 15000); /* * Query or change the count-changes flag. Normally, when the count-changes flag is not set, INSERT, UPDATE and * DELETE statements return no data. When count-changes is set, each of these commands returns a single row of * data consisting of one integer value - the number of rows inserted, modified or deleted by the command. The * returned change count does not include any insertions, modifications or deletions performed by triggers. */ // sqlite3_exec(sdb, "PRAGMA count_changes = OFF", NULL, NULL, NULL); ejsSetProperty(ejs, (EjsVar*) db, ES_ejs_db_Database__connection, (EjsVar*) ejsCreateString(ejs, path)); ejsSetProperty(ejs, (EjsVar*) db, ES_ejs_db_Database__name, (EjsVar*) ejsCreateString(ejs, mprGetBaseName(path))); return 0; }
/* * Get a property. */ static EjsVar *getErrorProperty(Ejs *ejs, EjsError *error, int slotNum) { switch (slotNum) { case ES_Error_stack: return (EjsVar*) ejsCreateString(ejs, error->stack); case ES_Error_message: return (EjsVar*) ejsCreateString(ejs, error->message); } return (ejs->objectHelpers->getProperty)(ejs, (EjsVar*) error, slotNum); }
/* * Create an exception object. */ static EjsVar *createException(Ejs *ejs, EjsType *type, cchar* fmt, va_list fmtArgs) { EjsVar *error, *argv[1]; char *msg; mprAssert(type); if (ejs->noExceptions) { return 0; } mprAllocVsprintf(ejs, &msg, MPR_MAX_STRING, fmt, fmtArgs); argv[0] = (EjsVar*) ejsCreateString(ejs, msg); if (argv[0] == 0) { mprAssert(argv[0]); return 0; } error = (EjsVar*) ejsCreateInstance(ejs, type, 1, argv); if (error == 0) { mprAssert(error); return 0; } mprFree(msg); return error; }
/* * Create a string variable */ static EjsVar *createString(Ejs *ejs, cchar *value) { if (value == 0) { return ejs->nullValue; } return (EjsVar*) ejsCreateString(ejs, value); }
/** Get a string representation of a variable. @return Returns a string variable or null if an exception is thrown. */ EjsString *ejsToString(Ejs *ejs, EjsVar *vp) { EjsFunction *fn; if (vp == 0) { return ejsCreateString(ejs, "undefined"); } else if (ejsIsString(vp)) { return (EjsString*) vp; } /* * Types can provide a toString method or a castVar helper */ if (vp->type->helpers->getProperty != getProperty) { fn = (EjsFunction*) ejsGetProperty(ejs, (EjsVar*) vp->type, ES_Object_toString); if (ejsIsFunction(fn)) { return (EjsString*) ejsRunFunction(ejs, fn, vp, 0, NULL); } } if (vp->type->helpers->castVar) { return (EjsString*) (vp->type->helpers->castVar)(ejs, vp, ejs->stringType); } ejsThrowInternalError(ejs, "CastVar helper not defined for type \"%s\"", vp->type->qname.name); return 0; }
/* * Return an array of table names in the database. * function get tables(): Array */ static EjsVar *tables(Ejs *ejs, EjsDb *db, int argc, EjsVar **argv) { EjsArray *ap; char **data, *error; int rc, rowCount, i; ejsSetDbMemoryContext(db->tls, db->arena); ap = ejsCreateArray(ejs, 0); rc = sqlite3_get_table(db->sdb, "SELECT name FROM sqlite_master " "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'" "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type IN ('table','view') " "ORDER BY 1", &data, &rowCount, 0, &error); if (error) { ejsThrowIOError(ejs, "%s", error); sqlite3_free(error); } if (rc == SQLITE_OK){ for (i = 1; i <= rowCount; i++) { ejsSetProperty(ejs, (EjsVar*) ap, i - 1, (EjsVar*) ejsCreateString(ejs, data[i])); } } sqlite3_free_table(data); return (EjsVar*) ap; }
/* * Joins the elements in the array into a single string. * @param sep Element separator. * @return Returns a string. * * function join(sep: String = undefined): String */ static EjsVar *joinArray(Ejs *ejs, EjsArray *ap, int argc, EjsVar **argv) { EjsString *result, *sep; EjsVar *vp; int i; if (argc == 1) { sep = (EjsString*) argv[0]; } else { sep = 0; } result = ejsCreateString(ejs, ""); for (i = 0; i < ap->length; i++) { vp = ap->data[i]; if (vp == 0 || ejsIsUndefined(vp) || ejsIsNull(vp)) { continue; } if (i > 0 && sep) { ejsStrcat(ejs, result, (EjsVar*) sep); } ejsStrcat(ejs, result, vp); } return (EjsVar*) result; }
EjsArray *ejsCaptureStack(Ejs *ejs, int uplevels) { EjsFrame *fp; EjsState *state; EjsArray *stack; wchar *source; EjsObj *frame; char *filename; int index, lineNumber; assert(ejs); stack = ejsCreateArray(ejs, 0); index = 0; for (state = ejs->state; state; state = state->prev) { for (fp = state->fp; fp; fp = fp->caller) { if (uplevels-- <= 0) { frame = ejsCreateEmptyPot(ejs); if (ejsGetDebugInfo(ejs, (EjsFunction*) fp, fp->pc, &filename, &lineNumber, &source) >= 0) { ejsSetPropertyByName(ejs, frame, EN("filename"), ejsCreatePathFromAsc(ejs, filename)); ejsSetPropertyByName(ejs, frame, EN("lineno"), ejsCreateNumber(ejs, lineNumber)); ejsSetPropertyByName(ejs, frame, EN("code"), ejsCreateString(ejs, source, wlen(source))); } else { ejsSetPropertyByName(ejs, frame, EN("filename"), EST(undefined)); } ejsSetPropertyByName(ejs, frame, EN("func"), fp->function.name); ejsSetProperty(ejs, stack, index++, frame); } } } return stack; }
static EjsVar *myEjs(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) { int i; /* Write this data back to the client */ ejsWrite(ejs, "<h1>Hello World</h1><p>Args: "); mprAssert(argv); for (i = 0; i < argc; ) { if (ejsIsNumber(argv[i])) { ejsWrite(ejs, "%d", ejsGetInt(argv[i])); } else { ejsWrite(ejs, ejsGetString(argv[i])); } if (++i < argc) { ejsWrite(ejs, " "); } } ejsWrite(ejs, "</p>"); /* * Functions can return a result */ return (EjsVar*) ejsCreateString(ejs, "sunny day"); return 0; }
/* * Public routine to set the error message. Caller MUST NOT free. */ char *ejsGetErrorMsg(Ejs *ejs, int withStack) { EjsVar *message, *stack, *error; cchar *name; char *buf; if (ejs->flags & EJS_FLAG_EMPTY) { return ""; } error = (EjsVar*) ejs->exception; message = stack = 0; name = 0; if (error) { name = error->type->qname.name; if (ejsIsA(ejs, error, ejs->errorType)) { message = ejsGetProperty(ejs, error, ES_Error_message); stack = ejsGetProperty(ejs, error, ES_Error_stack); } else if (ejsIsString(error)) { name = "Error"; message = error; } else if (ejsIsNumber(error)) { name = "Error"; message = error; } else if (error == (EjsVar*) ejs->stopIterationType) { name = "StopIteration"; message = (EjsVar*) ejsCreateString(ejs, "Uncaught StopIteration exception"); } } if (!withStack) { stack = 0; } if (stack && ejsIsString(stack) && message && ejsIsString(message)){ buf = mprAsprintf(ejs, -1, "%s Exception: %s\nStack:\n%s", name, ((EjsString*) message)->value, ((EjsString*) stack)->value); } else if (message && ejsIsString(message)){ buf = mprAsprintf(ejs, -1, "%s: %s", name, ((EjsString*) message)->value); } else if (message && ejsIsNumber(message)){ buf = mprAsprintf(ejs, -1, "%s: %d", name, ((EjsNumber*) message)->value); } else { if (error) { buf = mprStrdup(ejs, "Unknown exception object type"); } else { buf = mprStrdup(ejs, ""); } } mprFree(ejs->errorMsg); ejs->errorMsg = buf; return buf; }
static EjsVar *castVar(Ejs *ejs, EjsVar *vp, EjsType *toType) { EjsString *result; char *buf; buf = mprAsprintf(ejs, -1, "[object %s]", vp->type->qname.name); result = ejsCreateString(ejs, buf); mprFree(buf); return (EjsVar*) result; }
/* * Get an environment var * * function getenv(key: String): String */ static EjsVar *getEnvVar(Ejs *ejs, EjsObject *app, int argc, EjsVar **argv) { cchar *value; value = getenv(ejsGetString(argv[0])); if (value == 0) { return (EjsVar*) ejs->nullValue; } return (EjsVar*) ejsCreateString(ejs, value); }
/* * Get the application command line arguments * * static function get args(): String */ static EjsVar *getArgs(Ejs *ejs, EjsObject *unused, int argc, EjsVar **argv) { EjsArray *args; int i; args = ejsCreateArray(ejs, ejs->argc); for (i = 0; i < ejs->argc; i++) { ejsSetProperty(ejs, (EjsVar*) args, i, (EjsVar*) ejsCreateString(ejs, ejs->argv[i])); } return (EjsVar*) args; }
static EjsVar *castVar(Ejs *ejs, EjsVar *vp, EjsType *toType) { EjsString *result; char *buf; /* * TODO - should support cast to Boolean and Number */ mprAllocSprintf(ejs, &buf, 0, "[object %s]", vp->type->qname.name); result = ejsCreateString(ejs, buf); mprFree(buf); return (EjsVar*) result; }
/* Parse a string based on formatting instructions and intelligently create a variable. Number formats: [(+|-)][0][OCTAL_DIGITS] [(+|-)][0][(x|X)][HEX_DIGITS] [(+|-)][DIGITS] [+|-][DIGITS][.][DIGITS][(e|E)[+|-]DIGITS] */ EjsVar *ejsParseVar(Ejs *ejs, cchar *buf, int preferredType) { int type; mprAssert(buf); type = preferredType; if (preferredType == ES_Void || preferredType < 0) { if ((*buf == '-' || *buf == '+') && isdigit((int) buf[1])) { type = ejs->numberType->id; } else if (!isdigit((int) *buf) && *buf != '.') { if (strcmp(buf, "true") == 0) { return (EjsVar*) ejs->trueValue; } else if (strcmp(buf, "false") == 0) { return (EjsVar*) ejs->falseValue; } type = ES_String; } else { type = ES_Number; } } switch (type) { case ES_Object: case ES_Void: case ES_Null: default: break; case ES_Number: return (EjsVar*) ejsCreateNumber(ejs, parseNumber(ejs, buf)); case ES_Boolean: return (EjsVar*) ejsCreateBoolean(ejs, parseBoolean(ejs, buf)); case ES_String: if (strcmp(buf, "null") == 0) { return (EjsVar*) ejsCreateNull(ejs); } else if (strcmp(buf, "undefined") == 0) { return (EjsVar*) ejsCreateUndefined(ejs); } return (EjsVar*) ejsCreateString(ejs, buf); } return (EjsVar*) ejsCreateUndefined(ejs); }
/* * A header object from a given hash table */ static EjsVar *createHeaderObject(Ejs *ejs, MprHashTable *table) { MprHash *hp; EjsVar *headers, *header; EjsName qname; int index; headers = (EjsVar*) ejsCreateArray(ejs, mprGetHashCount(table)); for (index = 0, hp = 0; (hp = mprGetNextHash(table, hp)) != 0; ) { header = (EjsVar*) ejsCreateSimpleObject(ejs); ejsSetPropertyByName(ejs, header, ejsName(&qname, "", hp->key), (EjsVar*) ejsCreateString(ejs, hp->data)); ejsSetProperty(ejs, headers, index++, header); } return headers; }
static EjsVar *castBooleanVar(Ejs *ejs, EjsBoolean *vp, EjsType *type) { mprAssert(ejsIsBoolean(vp)); switch (type->id) { case ES_Number: return (EjsVar*) ((vp->value) ? ejs->oneValue: ejs->zeroValue); case ES_String: return (EjsVar*) ejsCreateString(ejs, (vp->value) ? "true" : "false"); default: ejsThrowTypeError(ejs, "Can't cast to this type"); return 0; } }
static EjsVar *castVoid(Ejs *ejs, EjsVoid *vp, EjsType *type) { switch (type->id) { case ES_Boolean: return (EjsVar*) ejs->falseValue; case ES_Number: return (EjsVar*) ejs->nanValue; case ES_Object: return vp; case ES_String: return (EjsVar*) ejsCreateString(ejs, "undefined"); default: ejsThrowTypeError(ejs, "Can't cast to this type"); return 0; } }
/* function exec(str: String, start: Number = 0): Array */ static EjsArray *regex_exec(Ejs *ejs, EjsRegExp *rp, int argc, EjsObj **argv) { EjsArray *results; EjsString *match, *str; int matches[BIT_MAX_REGEX_MATCHES * 3]; int count, start, len, i, index; str = (EjsString*) argv[0]; if (argc == 2) { start = (int) ejsGetNumber(ejs, argv[1]); } else { start = rp->endLastMatch; } rp->matched = 0; assert(rp->compiled); count = pcre_exec(rp->compiled, NULL, str->value, (int) str->length, start, 0, matches, sizeof(matches) / sizeof(int)); if (count < 0) { rp->endLastMatch = 0; return ESV(null); } results = ejsCreateArray(ejs, count); for (index = 0, i = 0; i < count; i++, index += 2) { len = matches[index + 1] - matches[index]; match = ejsCreateString(ejs, &str->value[matches[index]], len); ejsSetProperty(ejs, results, i, match); if (index == 0) { rp->matched = match; } } if (rp->global) { /* Only save if global flag used as per spec */ rp->startLastMatch = matches[0]; rp->endLastMatch = matches[1]; } return results; }
static EjsVar *castError(Ejs *ejs, EjsError *vp, EjsType *type) { EjsVar *sp; char *buf; switch (type->id) { case ES_Boolean: return (EjsVar*) ejsCreateBoolean(ejs, 1); case ES_String: if ((buf = mprAsprintf(ejs, -1, "%s Exception: %s\nStack:\n%s\n", vp->obj.var.type->qname.name, vp->message, vp->stack)) == NULL) { ejsThrowMemoryError(ejs); } sp = (EjsVar*) ejsCreateString(ejs, buf); mprFree(buf); return sp; default: ejsThrowTypeError(ejs, "Unknown type"); return 0; } }
static EjsString *regex_getSource(Ejs *ejs, EjsRegExp *rp, int argc, EjsObj **argv) { return ejsCreateString(ejs, rp->pattern, wlen(rp->pattern)); }
/* * function sql(cmd: String): Array * * Will support multiple sql cmds but will only return one result table. */ static EjsVar *sql(Ejs *ejs, EjsDb *db, int argc, EjsVar **argv) { sqlite3 *sdb; sqlite3_stmt *stmt; EjsArray *result; EjsVar *row, *svalue; EjsName qname; cchar *tail, *colName, *cmd, *value; int i, ncol, rc, retries, rowNum; mprAssert(ejs); mprAssert(db); cmd = ejsGetString(argv[0]); ejsSetDbMemoryContext(db->tls, db->arena); rc = SQLITE_OK; retries = 0; sdb = db->sdb; if (sdb == 0) { ejsThrowIOError(ejs, "Database is closed"); return 0; } mprAssert(sdb); result = ejsCreateArray(ejs, 0); if (result == 0) { return 0; } while (cmd && *cmd && (rc == SQLITE_OK || (rc == SQLITE_SCHEMA && ++retries < 2))) { stmt = 0; rc = sqlite3_prepare_v2(sdb, cmd, -1, &stmt, &tail); if (rc != SQLITE_OK) { continue; } if (stmt == 0) { /* Comment or white space */ cmd = tail; continue; } ncol = sqlite3_column_count(stmt); for (rowNum = 0; ; rowNum++) { rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { row = (EjsVar*) ejsCreateSimpleObject(ejs); if (row == 0) { sqlite3_finalize(stmt); return 0; } if (ejsSetProperty(ejs, (EjsVar*) result, rowNum, row) < 0) { /* TODO rc */ } for (i = 0; i < ncol; i++) { colName = sqlite3_column_name(stmt, i); value = (cchar*) sqlite3_column_text(stmt, i); ejsName(&qname, EJS_EMPTY_NAMESPACE, mprStrdup(row, colName)); if (ejsLookupProperty(ejs, row, &qname) < 0) { svalue = (EjsVar*) ejsCreateString(ejs, mprStrdup(row, value)); if (ejsSetPropertyByName(ejs, row, &qname, svalue) < 0) { /* TODO */ } } } } else { rc = sqlite3_finalize(stmt); stmt = 0; if (rc != SQLITE_SCHEMA) { // TODO - what is this? retries = 0; for (cmd = tail; isspace(*cmd); cmd++) { ; } } break; } #if UNUSED /* TODO -- should we be doing this */ cmd = tail; #endif } } if (stmt) { rc = sqlite3_finalize(stmt); } if (rc != SQLITE_OK) { if (rc == sqlite3_errcode(sdb)) { ejsThrowIOError(ejs, "SQL error: %s", sqlite3_errmsg(sdb)); } else { ejsThrowIOError(ejs, "Unspecified SQL error"); } return 0; } return (EjsVar*) 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; }
/* Parse an object literal string pointed to by js->next into the given buffer. Update js->next to point to the next input token in the object literal. Supports nested object literals. */ static EjsObj *parseLiteralInner(Ejs *ejs, MprBuf *buf, JsonState *js) { EjsAny *obj, *vp; MprBuf *valueBuf; wchar *token, *key, *value; int tid, isArray; isArray = 0; tid = getNextJsonToken(buf, &token, js); if (tid == TOK_ERR || tid == TOK_EOF) { return 0; } if (tid == TOK_LBRACKET) { isArray = 1; obj = (EjsObj*) ejsCreateArray(ejs, 0); } else if (tid == TOK_LBRACE) { obj = ejsCreateEmptyPot(ejs); } else { return ejsParse(ejs, token, S_String); } if (obj == 0) { ejsThrowMemoryError(ejs); return 0; } while (1) { vp = 0; tid = peekNextJsonToken(js); if (tid == TOK_ERR) { return 0; } else if (tid == TOK_EOF) { break; } else if (tid == TOK_RBRACE || tid == TOK_RBRACKET) { getNextJsonToken(buf, &key, js); break; } if (tid == TOK_LBRACKET) { /* For array values */ vp = parseLiteral(ejs, js); assert(vp); } else if (tid == TOK_LBRACE) { /* For object values */ vp = parseLiteral(ejs, js); assert(vp); } else if (isArray) { tid = getNextJsonToken(buf, &value, js); vp = ejsParse(ejs, value, (tid == TOK_QID) ? S_String: -1); assert(vp); } else { getNextJsonToken(buf, &key, js); tid = peekNextJsonToken(js); if (tid == TOK_ERR) { return 0; } else if (tid == TOK_EOF) { break; } else if (tid == TOK_LBRACE || tid == TOK_LBRACKET) { vp = parseLiteral(ejs, js); } else if (tid == TOK_ID || tid == TOK_QID) { valueBuf = mprCreateBuf(0, 0); getNextJsonToken(valueBuf, &value, js); if (tid == TOK_QID) { vp = ejsCreateString(ejs, value, strlen(value)); } else { if (mcmp(value, "null") == 0) { vp = ESV(null); } else if (mcmp(value, "undefined") == 0) { vp = ESV(undefined); } else { vp = ejsParse(ejs, value, -1); } } assert(vp); } else { getNextJsonToken(buf, &value, js); js->error = js->next; return 0; } } if (vp == 0) { js->error = js->next; return 0; } if (isArray) { if (ejsSetProperty(ejs, obj, -1, vp) < 0) { ejsThrowMemoryError(ejs); return 0; } } else { if (ejsSetPropertyByName(ejs, obj, WEN(key), vp) < 0) { ejsThrowMemoryError(ejs); return 0; } } } return obj; }
/* Cast the instance to another type. @param ejs VM handle. @param sp is set to the object instance. @returns the function result or 0 if an exception is thrown. */ static EjsObj *castVar(Ejs *ejs, Shape *sp, EjsType *type) { return (EjsObj*) ejsCreateString(ejs, "[object Shape]"); }
/* * Parse a string based on formatting instructions and intelligently create a variable. * * Float and decimal format: [+|-]DIGITS][DIGITS][(e|E)[+|-]DIGITS] * * TODO - refactor all this number parsing. */ EjsVar *ejsParseVar(Ejs *ejs, cchar *buf, int preferredType) { cchar *cp; int isHex, type; mprAssert(buf); type = preferredType; isHex = 0; if (preferredType == ES_Void || preferredType < 0) { if (*buf == '-' || *buf == '+') { type = ejs->numberType->id; } else if (!isdigit((int) *buf)) { if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) { type = ES_Boolean; } else { type = ES_String; } } else { if (buf[0] == '0' && tolower((int) buf[1]) == 'x') { isHex = 1; for (cp = &buf[2]; *cp; cp++) { if (! isxdigit((int) *cp)) { break; } } } else { for (cp = buf; *cp; cp++) { if (! isdigit((int) *cp)) { #if BLD_FEATURE_FLOATING_POINT int c = tolower((int) *cp); if (c != '.' && c != 'e' && c != 'f') #endif break; } } } if (*cp == '\0') { type = ES_Number; } else { type = ES_String; } } } switch (type) { case ES_Object: case ES_Void: case ES_Null: default: break; case ES_Number: return (EjsVar*) ejsCreateNumber(ejs, parseNumber(ejs, buf, isHex)); case ES_Boolean: return (EjsVar*) ejsCreateBoolean(ejs, parseBoolean(ejs, buf)); case ES_String: if (strcmp(buf, "null") == 0) { return (EjsVar*) ejsCreateNull(ejs); } else if (strcmp(buf, "undefined") == 0) { return (EjsVar*) ejsCreateUndefined(ejs); } return (EjsVar*) ejsCreateString(ejs, buf); } return (EjsVar*) ejsCreateUndefined(ejs); }
/* Set a property attribute by name. */ static int setXmlPropertyAttributeByName(Ejs *ejs, EjsXML *xml, EjsName qname, EjsObj *value) { EjsXML *elt, *attribute, *xvalue, *lastElt; EjsString *sv; EjsName qn; wchar *str; int index, last, next; /* Attribute. If the value is an XML list, convert to a space separated string */ xvalue = (EjsXML*) value; if (ejsIsXML(ejs, xvalue) && xvalue->kind == EJS_XML_LIST) { str = 0; for (next = 0; (elt = mprGetNextItem(xvalue->elements, &next)) != 0; ) { sv = (EjsString*) ejsCast(ejs, (EjsObj*) elt, String); str = mrejoin(str, NULL, " ", sv->value, NULL); } value = (EjsObj*) ejsCreateString(ejs, str, -1); } else { value = ejsCast(ejs, value, String); } assert(ejsIs(ejs, value, String)); /* Find the first attribute that matches. Delete all other attributes of the same name. */ index = 0; if (xml->attributes) { lastElt = 0; for (last = -1, index = -1; (elt = mprGetPrevItem(xml->attributes, &index)) != 0; ) { assert(qname.name->value[0] == '@'); if (wcmp(elt->qname.name->value, &qname.name->value[1]) == 0) { if (last >= 0) { mprRemoveItemAtPos(xml->attributes, last); } last = index; lastElt = elt; } } if (lastElt) { /* Found a match. So replace its value */ lastElt->value = (EjsString*) value; return last; } else { index = mprGetListLength(xml->attributes); } } // TODO - namespace work to do here /* Not found. Create a new attribute node */ assert(ejsIs(ejs, value, String)); qn.space = NULL; qn.name = ejsSubstring(ejs, qname.name, 1, -1); attribute = ejsCreateXML(ejs, EJS_XML_ATTRIBUTE, qn, xml, (EjsString*) value); if (xml->attributes == 0) { xml->attributes = mprCreateList(-1, 0); } mprSetItem(xml->attributes, index, attribute); return index; }
/* * Get the ejs module search path (EJSPATH). Does not actually read the environment. * * function get searchPath(): String */ static EjsVar *getSearchPath(Ejs *ejs, EjsObject *app, int argc, EjsVar **argv) { return (EjsVar*) ejsCreateString(ejs, ejs->ejsPath); }