/* * Post a message to this worker. Note: the worker is the destination worker which may be the parent. * * function postMessage(data: Object, ports: Array = null): Void */ static EjsVar *workerPostMessage(Ejs *ejs, EjsWorker *worker, int argc, EjsVar **argv) { EjsVar *data; EjsWorker *target; MprDispatcher *dispatcher; Message *msg; if (worker->state >= EJS_WORKER_CLOSED) { ejsThrowStateError(ejs, "Worker has completed"); return 0; } /* * Create the event with serialized data in the originating interpreter. It owns the data. */ if ((data = ejsSerialize(ejs, argv[0], -1, 0, 0)) == 0) { ejsThrowArgError(ejs, "Can't serialize message data"); return 0; } if ((msg = mprAllocObjZeroed(ejs, Message)) == 0) { ejsThrowMemoryError(ejs); return 0; } target = worker->pair; msg->data = mprStrdup(target->ejs, ejsGetString(data)); msg->worker = target; msg->callback = "onmessage"; msg->callbackSlot = ES_ejs_sys_Worker_onmessage; dispatcher = target->ejs->dispatcher; mprCreateEvent(dispatcher, (MprEventProc) doMessage, 0, MPR_NORMAL_PRIORITY, msg, 0); mprSignalCond(dispatcher->cond); return 0; }
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; }
/* * Put an environment var * * function putenv(key: String, value: String): void */ static EjsVar *putEnvVar(Ejs *ejs, EjsObject *app, int argc, EjsVar **argv) { #if !WINCE #if BLD_UNIX_LIKE char *key, *value; key = mprStrdup(ejs, ejsGetString(argv[0])); value = mprStrdup(ejs, ejsGetString(argv[1])); setenv(key, value, 1); #else char *cmd; cmd = mprStrcat(app, -1, ejsGetString(argv[0]), "=", ejsGetString(argv[1]), NULL); putenv(cmd); #endif #endif return 0; }
/* * 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); }
/* * function eval(script: String, timeout: Boolean = -1): String */ static EjsVar *workerEval(Ejs *ejs, EjsWorker *worker, int argc, EjsVar **argv) { int timeout; mprAssert(ejsIsString(argv[0])); worker->scriptLiteral = mprStrdup(worker, ejsGetString(argv[0])); timeout = argc == 2 ? ejsGetInt(argv[1]): MAXINT; return startWorker(ejs, worker, timeout); }
/* * 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; }
/* * Error Constructor and constructor for all the core error classes. * * public function Error(message: String = null) */ static EjsVar *errorConstructor(Ejs *ejs, EjsError *error, int argc, EjsVar **argv) { mprFree(error->message); if (argc == 0) { error->message = mprStrdup(error, ""); } else { error->message = mprStrdup(error, ejsGetString(argv[0])); } mprFree(error->stack); ejsFormatStack(ejs, error); return (EjsVar*) error; }
/* * WARNING: the inside interpreter owns the exception object. Must fully extract all fields */ static void handleError(Ejs *ejs, EjsWorker *worker, EjsVar *exception) { EjsError *error; MprDispatcher *dispatcher; Message *msg; mprAssert(!worker->inside); mprAssert(exception); mprAssert(ejs == worker->ejs); if ((msg = mprAllocObjZeroed(ejs, Message)) == 0) { ejsThrowMemoryError(ejs); return; } msg->worker = worker; msg->callback = "onerror"; msg->callbackSlot = ES_ejs_sys_Worker_onerror; /* * Inside interpreter owns the exception object, so must fully extract all exception. * Allocate into the outside worker's interpreter. */ if (ejsIsError(exception)) { error = (EjsError*) exception; msg->message = mprStrdup(ejs, error->message); msg->filename = mprStrdup(ejs, error->filename ? error->filename : "script"); msg->lineNumber = error->lineNumber; msg->stack = mprStrdup(ejs, error->stack); } else if (ejsIsString(exception)) { msg->message = mprStrdup(ejs, ejsGetString(exception)); } else { msg->message = mprStrdup(ejs, ejsGetString(ejsToString(ejs, exception))); } dispatcher = ejs->dispatcher; mprCreateEvent(dispatcher, (MprEventProc) doMessage, 0, MPR_NORMAL_PRIORITY, msg, 0); mprSignalCond(dispatcher->cond); }
/* DB Constructor and also used for constructor for sub classes. function Debugger(connectionString: String) */ static EjsVar *debuggerConstructor(Ejs *ejs, EjsDebugger *db, int argc, EjsVar **argv) { debugger3 *sdb; cchar *path; path = ejsGetString(ejs, argv[0]); db->ejs = ejs; /* * Create a memory context for use by debugger. This is a virtual paged memory region. * TODO OPT - Could do better for running applications. */ #if MAP_ALLOC db->arena = mprAllocArena(ejs, "debugger", EJS_MAX_SQLITE_MEM, !USE_TLS, 0); if (db->arena == 0) { return 0; } SET_CTX(db->arena); #else db->arena = mprAllocHeap(ejs, "debugger", EJS_MAX_SQLITE_MEM, 1, 0); if (db->arena == 0) { return 0; } SET_CTX(db->arena); #endif #if UNUSED EjsDebugger **dbp; /* * 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 = mprAllocWithDestructor(ejs, sizeof(void*), (MprDestructor) sqldbDestructor)) == 0) { ejsThrowMemoryError(ejs); return 0; } *dbp = db; #endif sdb = 0; if (debugger3_open(path, &sdb) != SQLITE_OK) { ejsThrowIOError(ejs, "Cannot open database %s", path); return 0; } db->sdb = sdb; debugger3_busy_timeout(sdb, ME_MAX_SQLITE_DURATION); debugger3_soft_heap_limit(ME_MAX_SQLITE_MEM); return 0; }
/* * static function lookup(name: String): Worker */ static EjsVar *workerLookup(Ejs *ejs, EjsVar *unused, int argc, EjsVar **argv) { EjsWorker *worker; cchar *name; int next; name = ejsGetString(argv[0]); lock(ejs); for (next = 0; (worker = mprGetNextItem(ejs->workers, &next)) != NULL; ) { if (worker->name && strcmp(name, worker->name) == 0) { unlock(ejs); return (EjsVar*) worker; } } unlock(ejs); return ejs->nullValue; }
/* * Set the current working directory * * function chdir(value: String|Path): void */ static EjsVar *changeCurrentDir(Ejs *ejs, EjsVar *unused, int argc, EjsVar **argv) { char *path; mprAssert(argc == 1); if (ejsIsPath(argv[0])) { path = ((EjsPath*) argv[0])->path; } else if (ejsIsString(argv[0])) { path = ejsGetString(argv[0]); } else { ejsThrowIOError(ejs, "Bad path"); return NULL; } if (chdir(path) < 0) { ejsThrowIOError(ejs, "Can't change the current directory"); } return 0; }
/* * 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; }
/* * Set the ejs module search path (EJSPATH). Does not actually update the environment. * * function set searchPath(path: String): Void */ static EjsVar *setSearchPath(Ejs *ejs, EjsObject *app, int argc, EjsVar **argv) { ejsSetSearchPath(ejs, ejsGetString(argv[0])); return 0; }
/* * function Worker(script: String = null, options: Object = null) * * Script is optional. If supplied, the script is run immediately by a worker thread. This call * does not block. Options are: search and name. */ static EjsVar *workerConstructor(Ejs *ejs, EjsWorker *worker, int argc, EjsVar **argv) { Ejs *wejs; EjsVar *options, *value; EjsName qname; EjsWorker *self; cchar *search, *name; worker->ejs = ejs; worker->state = EJS_WORKER_BEGIN; options = (argc == 2) ? (EjsVar*) argv[1]: NULL; name = 0; search = ejs->ejsPath; if (options) { value = ejsGetPropertyByName(ejs, options, ejsName(&qname, "", "search")); if (ejsIsString(value)) { search = ejsGetString(value); } value = ejsGetPropertyByName(ejs, options, ejsName(&qname, "", "name")); if (ejsIsString(value)) { name = ejsGetString(value); } } if (name) { worker->name = mprStrdup(worker, name); } else { worker->name = mprAsprintf(worker, -1, "worker-%d", mprGetListCount(ejs->workers)); } /* * Create a new interpreter and an "inside" worker object and pair it with the current "outside" worker. */ wejs = ejsCreate(ejs->service, NULL, search, 0); if (wejs == 0) { ejsThrowMemoryError(ejs); return 0; } worker->pair = self = ejsCreateWorker(wejs); self->state = EJS_WORKER_BEGIN; self->ejs = wejs; self->inside = 1; self->pair = worker; self->name = mprStrcat(self, -1, "inside-", worker->name, NULL); ejsSetProperty(ejs, (EjsVar*) worker, ES_ejs_sys_Worker_name, (EjsVar*) ejsCreateString(ejs, self->name)); ejsSetProperty(wejs, (EjsVar*) self, ES_ejs_sys_Worker_name, (EjsVar*) ejsCreateString(wejs, self->name)); ejsSetProperty(wejs, wejs->global, ES_ejs_sys_worker_self, (EjsVar*) self); /* * Workers have a dedicated namespace to enable viewing of the worker globals (self, onmessage, postMessage...) */ ejsDefineReservedNamespace(wejs, wejs->globalBlock, 0, EJS_WORKER_NAMESPACE); /* * Make the inside worker permanent so we don't need to worry about whether worker->pair->ejs is valid */ self->obj.var.permanent = 1; if (argc > 0 && ejsIsPath(argv[0])) { addWorker(ejs, worker); worker->scriptFile = mprStrdup(worker, ((EjsPath*) argv[0])->path); worker->state = EJS_WORKER_STARTED; worker->obj.var.permanent = 1; if (mprStartWorker(ejs, (MprWorkerProc) workerMain, (void*) worker, MPR_NORMAL_PRIORITY) < 0) { ejsThrowStateError(ejs, "Can't start worker"); worker->obj.var.permanent = 0; return 0; } } return (EjsVar*) worker; }