// Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include #include #include #include "http.h" #include "sm.h" #include "utf8.h" #include "util.h" #ifdef JS_THREADSAFE #define SETUP_REQUEST(cx) \ JS_SetContextThread(cx); \ JS_BeginRequest(cx); #define FINISH_REQUEST(cx) \ JS_EndRequest(cx); \ JS_ClearContextThread(cx); #else #define SETUP_REQUEST(cx) #define FINISH_REQUEST(cx) #endif static JSBool req_ctor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { return http_ctor(cx, obj); } static void req_dtor(JSContext* cx, JSObject* obj) { http_dtor(cx, obj); } static JSBool req_open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JSBool ret = JS_FALSE; if(argc == 2) { ret = http_open(cx, obj, argv[0], argv[1], JSVAL_FALSE); } else if(argc == 3) { ret = http_open(cx, obj, argv[0], argv[1], argv[2]); } else { JS_ReportError(cx, "Invalid call to CouchHTTP.open"); } *rval = JSVAL_VOID; return ret; } static JSBool req_set_hdr(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JSBool ret = JS_FALSE; if(argc == 2) { ret = http_set_hdr(cx, obj, argv[0], argv[1]); } else { JS_ReportError(cx, "Invalid call to CouchHTTP.set_header"); } *rval = JSVAL_VOID; return ret; } static JSBool req_send(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JSBool ret = JS_FALSE; if(argc == 1) { ret = http_send(cx, obj, argv[0]); } else { JS_ReportError(cx, "Invalid call to CouchHTTP.send"); } *rval = JSVAL_VOID; return ret; } static JSBool req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* rval) { int status = http_status(cx, obj); if(status < 0) return JS_FALSE; if(INT_FITS_IN_JSVAL(status)) { *rval = INT_TO_JSVAL(status); return JS_TRUE; } else { JS_ReportError(cx, "Invalid HTTP status."); return JS_FALSE; } } static JSBool evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; JSObject *sandbox; JSContext *subcx; const jschar *src; size_t srclen; JSBool ret = JS_FALSE; couch_args* args = (couch_args*) JS_GetContextPrivate(cx); sandbox = NULL; if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) { return JS_FALSE; } subcx = JS_NewContext(JS_GetRuntime(cx), args->stack_size); if(!subcx) { JS_ReportOutOfMemory(cx); return JS_FALSE; } SETUP_REQUEST(subcx); src = JS_GetStringChars(str); srclen = JS_GetStringLength(str); if(!sandbox) { sandbox = JS_NewObject(subcx, NULL, NULL, NULL); if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) { goto done; } } if(srclen == 0) { *rval = OBJECT_TO_JSVAL(sandbox); } else { JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, rval); } ret = JS_TRUE; done: FINISH_REQUEST(subcx); JS_DestroyContext(subcx); return ret; } static JSBool gc(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JS_GC(cx); *rval = JSVAL_VOID; return JS_TRUE; } static JSBool print(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { couch_print(cx, argc, argv); *rval = JSVAL_VOID; return JS_TRUE; } static JSBool quit(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { int exit_code = 0; JS_ConvertArguments(cx, argc, argv, "/i", &exit_code); exit(exit_code); } static JSBool readline(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JSString* line; /* GC Occasionally */ JS_MaybeGC(cx); line = couch_readline(cx, stdin); if(line == NULL) return JS_FALSE; *rval = STRING_TO_JSVAL(line); return JS_TRUE; } static JSBool seal(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { JSObject *target; JSBool deep = JS_FALSE; if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) return JS_FALSE; if(!target) { *rval = JSVAL_VOID; return JS_TRUE; } if(JS_SealObject(cx, target, deep) != JS_TRUE) return JS_FALSE; *rval = JSVAL_VOID; return JS_TRUE; } JSClass CouchHTTPClass = { "CouchHTTP", JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_RESERVED_SLOTS(2), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, req_dtor, JSCLASS_NO_OPTIONAL_MEMBERS }; JSPropertySpec CouchHTTPProperties[] = { {"status", 0, JSPROP_READONLY, req_status, NULL}, {0, 0, 0, 0, 0} }; JSFunctionSpec CouchHTTPFunctions[] = { {"_open", req_open, 3, 0, 0}, {"_setRequestHeader", req_set_hdr, 2, 0, 0}, {"_send", req_send, 1, 0, 0}, {0, 0, 0, 0, 0} }; static JSClass global_class = { "GlobalClass", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSFunctionSpec global_functions[] = { {"evalcx", evalcx, 0, 0, 0}, {"gc", gc, 0, 0, 0}, {"print", print, 0, 0, 0}, {"quit", quit, 0, 0, 0}, {"readline", readline, 0, 0, 0}, {"seal", seal, 0, 0, 0}, {0, 0, 0, 0, 0} }; int main(int argc, const char* argv[]) { JSRuntime* rt = NULL; JSContext* cx = NULL; JSObject* global = NULL; JSObject* klass = NULL; JSScript* script; JSString* scriptsrc; jschar* schars; size_t slen; jsval sroot; jsval result; couch_args* args = couch_parse_args(argc, argv); rt = JS_NewRuntime(64L * 1024L * 1024L); if(rt == NULL) return 1; cx = JS_NewContext(rt, args->stack_size); if(cx == NULL) return 1; JS_SetContextPrivate(cx, args); JS_SetErrorReporter(cx, couch_error); JS_ToggleOptions(cx, JSOPTION_XML); SETUP_REQUEST(cx); global = JS_NewObject(cx, &global_class, NULL, NULL); if(global == NULL) return 1; JS_SetGlobalObject(cx, global); if(!JS_InitStandardClasses(cx, global)) return 1; if(couch_load_funcs(cx, global, global_functions) != JS_TRUE) return 1; if(args->use_http) { http_check_enabled(); klass = JS_InitClass( cx, global, NULL, &CouchHTTPClass, req_ctor, 0, CouchHTTPProperties, CouchHTTPFunctions, NULL, NULL ); if(!klass) { fprintf(stderr, "Failed to initialize CouchHTTP class.\n"); exit(2); } } // Convert script source to jschars. scriptsrc = dec_string(cx, args->script, strlen(args->script)); if(!scriptsrc) return 1; schars = JS_GetStringChars(scriptsrc); slen = JS_GetStringLength(scriptsrc); // Root it so GC doesn't collect it. sroot = STRING_TO_JSVAL(scriptsrc); if(JS_AddRoot(cx, &sroot) != JS_TRUE) { fprintf(stderr, "Internal root error.\n"); return 1; } // Compile and run script = JS_CompileUCScript(cx, global, schars, slen, args->script_name, 1); if(!script) { fprintf(stderr, "Failed to compile script.\n"); return 1; } JS_ExecuteScript(cx, global, script, &result); // Warning message if we don't remove it. JS_RemoveRoot(cx, &sroot); FINISH_REQUEST(cx); JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); return 0; }