From 6cf2f036ece28c95988b8312c8532fac55a3c7a0 Mon Sep 17 00:00:00 2001 From: Paul Joseph Davis Date: Thu, 8 Sep 2011 05:05:19 +0000 Subject: And the rest of the commit/ SVN != Git. Forgot to check svn status before committing. git-svn-id: https://svn.apache.org/repos/asf/couchdb/branches/1.1.x@1166526 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/priv/couch_js/sm170.c | 378 +++++++++++++++++++++++++++++++++++ src/couchdb/priv/couch_js/sm180.c | 387 ++++++++++++++++++++++++++++++++++++ src/couchdb/priv/couch_js/sm185.c | 401 ++++++++++++++++++++++++++++++++++++++ src/couchdb/priv/couch_js/util.c | 237 ++++++++++++++++++++++ src/couchdb/priv/couch_js/util.h | 34 ++++ 5 files changed, 1437 insertions(+) create mode 100644 src/couchdb/priv/couch_js/sm170.c create mode 100644 src/couchdb/priv/couch_js/sm180.c create mode 100644 src/couchdb/priv/couch_js/sm185.c create mode 100644 src/couchdb/priv/couch_js/util.c create mode 100644 src/couchdb/priv/couch_js/util.h diff --git a/src/couchdb/priv/couch_js/sm170.c b/src/couchdb/priv/couch_js/sm170.c new file mode 100644 index 00000000..ebb6673f --- /dev/null +++ b/src/couchdb/priv/couch_js/sm170.c @@ -0,0 +1,378 @@ +// 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 +#include "http.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; + + sandbox = NULL; + if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) { + return JS_FALSE; + } + + subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L); + 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, obj, 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_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; +} diff --git a/src/couchdb/priv/couch_js/sm180.c b/src/couchdb/priv/couch_js/sm180.c new file mode 100644 index 00000000..dee16a78 --- /dev/null +++ b/src/couchdb/priv/couch_js/sm180.c @@ -0,0 +1,387 @@ +// 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 +#include "http.h" +#include "utf8.h" +#include "util.h" + + +#define SETUP_REQUEST(cx) \ + JS_SetContextThread(cx); \ + JS_BeginRequest(cx); +#define FINISH_REQUEST(cx) \ + JS_EndRequest(cx); \ + JS_ClearContextThread(cx); + + +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, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + 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"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_set_hdr(JSContext* cx, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + 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"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_send(JSContext* cx, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + JSBool ret = JS_FALSE; + + if(argc == 1) { + ret = http_send(cx, obj, argv[0]); + } else { + JS_ReportError(cx, "Invalid call to CouchHTTP.send"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) +{ + int status = http_status(cx, obj); + if(status < 0) + return JS_FALSE; + + if(INT_FITS_IN_JSVAL(status)) { + JS_SET_RVAL(cx, vp, INT_TO_JSVAL(status)); + return JS_TRUE; + } else { + JS_ReportError(cx, "Invalid HTTP status."); + return JS_FALSE; + } +} + + +static JSBool +evalcx(JSContext *cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + JSString *str; + JSObject *sandbox; + JSContext *subcx; + const jschar *src; + size_t srclen; + jsval rval; + JSBool ret = JS_FALSE; + + sandbox = NULL; + if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) { + return JS_FALSE; + } + + subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L); + 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) { + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sandbox)); + } else { + JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, &rval); + JS_SET_RVAL(cx, vp, rval); + } + + ret = JS_TRUE; + +done: + FINISH_REQUEST(subcx); + JS_DestroyContext(subcx); + return ret; +} + + +static JSBool +gc(JSContext* cx, uintN argc, jsval* vp) +{ + JS_GC(cx); + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; +} + + +static JSBool +print(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + couch_print(cx, argc, argv); + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; +} + + +static JSBool +quit(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + int exit_code = 0; + JS_ConvertArguments(cx, argc, argv, "/i", &exit_code); + exit(exit_code); +} + + +static JSBool +readline(JSContext* cx, uintN argc, jsval* vp) +{ + JSString* line; + + /* GC Occasionally */ + JS_MaybeGC(cx); + + line = couch_readline(cx, stdin); + if(line == NULL) return JS_FALSE; + + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(line)); + return JS_TRUE; +} + + +static JSBool +seal(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + JSObject *target; + JSBool deep = JS_FALSE; + + if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) + return JS_FALSE; + + if(!target) { + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; + } + + if(JS_SealObject(cx, target, deep) != JS_TRUE) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, 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[] = { + JS_FS("_open", (JSNative) req_open, 3, JSFUN_FAST_NATIVE, 0), + JS_FS("_setRequestHeader", (JSNative) req_set_hdr, 2, JSFUN_FAST_NATIVE, 0), + JS_FS("_send", (JSNative) req_send, 1, JSFUN_FAST_NATIVE, 0), + JS_FS_END +}; + + +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[] = { + JS_FS("evalcx", (JSNative) evalcx, 0, JSFUN_FAST_NATIVE, 0), + JS_FS("gc", (JSNative) gc, 0, JSFUN_FAST_NATIVE, 0), + JS_FS("print", (JSNative) print, 0, JSFUN_FAST_NATIVE, 0), + JS_FS("quit", (JSNative) quit, 0, JSFUN_FAST_NATIVE, 0), + JS_FS("readline", (JSNative) readline, 0, JSFUN_FAST_NATIVE, 0), + JS_FS("seal", (JSNative) seal, 0, JSFUN_FAST_NATIVE, 0), + JS_FS_END +}; + + +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_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; +} diff --git a/src/couchdb/priv/couch_js/sm185.c b/src/couchdb/priv/couch_js/sm185.c new file mode 100644 index 00000000..701d5677 --- /dev/null +++ b/src/couchdb/priv/couch_js/sm185.c @@ -0,0 +1,401 @@ +// 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 +#include "http.h" +#include "utf8.h" +#include "util.h" + + +#define SETUP_REQUEST(cx) \ + JS_SetContextThread(cx); \ + JS_BeginRequest(cx); +#define FINISH_REQUEST(cx) \ + JS_EndRequest(cx); \ + JS_ClearContextThread(cx); + + +static JSClass global_class = { + "GlobalClass", + JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, + JS_PropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + + +static JSBool +req_ctor(JSContext* cx, uintN argc, jsval* vp) +{ + JSBool ret; + JSObject* obj = JS_NewObjectForConstructor(cx, vp); + if(!obj) { + JS_ReportError(cx, "Failed to create CouchHTTP instance.\n"); + return JS_FALSE; + } + ret = http_ctor(cx, obj); + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); + return ret; +} + + +static void +req_dtor(JSContext* cx, JSObject* obj) +{ + http_dtor(cx, obj); +} + + +static JSBool +req_open(JSContext* cx, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + 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"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_set_hdr(JSContext* cx, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + 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"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_send(JSContext* cx, uintN argc, jsval* vp) +{ + JSObject* obj = JS_THIS_OBJECT(cx, vp); + jsval* argv = JS_ARGV(cx, vp); + JSBool ret = JS_FALSE; + + if(argc == 1) { + ret = http_send(cx, obj, argv[0]); + } else { + JS_ReportError(cx, "Invalid call to CouchHTTP.send"); + } + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +static JSBool +req_status(JSContext* cx, JSObject* obj, jsid pid, jsval* vp) +{ + int status = http_status(cx, obj); + if(status < 0) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, INT_TO_JSVAL(status)); + return JS_TRUE; +} + + +static JSBool +evalcx(JSContext *cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + JSString* str; + JSObject* sandbox; + JSObject* global; + JSContext* subcx; + JSCrossCompartmentCall* call = NULL; + const jschar* src; + size_t srclen; + jsval rval; + JSBool ret = JS_FALSE; + + sandbox = NULL; + if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) { + return JS_FALSE; + } + + subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L); + if(!subcx) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + SETUP_REQUEST(subcx); + + src = JS_GetStringCharsAndLength(cx, str, &srclen); + + // Re-use the compartment associated with the main context, + // rather than creating a new compartment */ + global = JS_GetGlobalObject(cx); + if(global == NULL) goto done; + call = JS_EnterCrossCompartmentCall(subcx, global); + + if(!sandbox) { + sandbox = JS_NewGlobalObject(subcx, &global_class); + if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) { + goto done; + } + } + + if(srclen == 0) { + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sandbox)); + } else { + JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, &rval); + JS_SET_RVAL(cx, vp, rval); + } + + ret = JS_TRUE; + +done: + JS_LeaveCrossCompartmentCall(call); + FINISH_REQUEST(subcx); + JS_DestroyContext(subcx); + return ret; +} + + +static JSBool +gc(JSContext* cx, uintN argc, jsval* vp) +{ + JS_GC(cx); + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; +} + + +static JSBool +print(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + couch_print(cx, argc, argv); + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; +} + + +static JSBool +quit(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + int exit_code = 0; + JS_ConvertArguments(cx, argc, argv, "/i", &exit_code); + exit(exit_code); +} + + +static JSBool +readline(JSContext* cx, uintN argc, jsval* vp) +{ + JSString* line; + + /* GC Occasionally */ + JS_MaybeGC(cx); + + line = couch_readline(cx, stdin); + if(line == NULL) return JS_FALSE; + + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(line)); + return JS_TRUE; +} + + +static JSBool +seal(JSContext* cx, uintN argc, jsval* vp) +{ + jsval* argv = JS_ARGV(cx, vp); + JSObject *target; + JSBool deep = JS_FALSE; + JSBool ret; + + if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) + return JS_FALSE; + + if(!target) { + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; + } + + + ret = deep ? JS_DeepFreezeObject(cx, target) : JS_FreezeObject(cx, target); + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return ret; +} + + +JSClass CouchHTTPClass = { + "CouchHTTP", + JSCLASS_HAS_PRIVATE + | JSCLASS_CONSTRUCT_PROTOTYPE + | JSCLASS_HAS_RESERVED_SLOTS(2), + JS_PropertyStub, + JS_PropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + 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[] = { + JS_FS("_open", req_open, 3, 0), + JS_FS("_setRequestHeader", req_set_hdr, 2, 0), + JS_FS("_send", req_send, 1, 0), + JS_FS_END +}; + + +static JSFunctionSpec global_functions[] = { + JS_FS("evalcx", evalcx, 0, 0), + JS_FS("gc", gc, 0, 0), + JS_FS("print", print, 0, 0), + JS_FS("quit", quit, 0, 0), + JS_FS("readline", readline, 0, 0), + JS_FS("seal", seal, 0, 0), + JS_FS_END +}; + + +int +main(int argc, const char* argv[]) +{ + JSRuntime* rt = NULL; + JSContext* cx = NULL; + JSObject* global = NULL; + JSCrossCompartmentCall *call = NULL; + JSObject* klass = NULL; + JSObject* script; + JSString* scriptsrc; + const 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_SetErrorReporter(cx, couch_error); + JS_ToggleOptions(cx, JSOPTION_XML); + + SETUP_REQUEST(cx); + + global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL); + if(global == NULL) + return 1; + + call = JS_EnterCrossCompartmentCall(cx, global); + + 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_GetStringCharsAndLength(cx, scriptsrc, &slen); + + // Root it so GC doesn't collect it. + sroot = STRING_TO_JSVAL(scriptsrc); + if(JS_AddValueRoot(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_RemoveValueRoot(cx, &sroot); + + JS_LeaveCrossCompartmentCall(call); + FINISH_REQUEST(cx); + JS_DestroyContext(cx); + JS_DestroyRuntime(rt); + JS_ShutDown(); + + return 0; +} diff --git a/src/couchdb/priv/couch_js/util.c b/src/couchdb/priv/couch_js/util.c new file mode 100644 index 00000000..070d7172 --- /dev/null +++ b/src/couchdb/priv/couch_js/util.c @@ -0,0 +1,237 @@ +// 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 "util.h" +#include "utf8.h" + + +char* +slurp_file(char* buf, const char* file) +{ + FILE* fp; + char fbuf[16384]; + char* tmp; + size_t nread = 0; + size_t buflen = 0; + + if(strcmp(file, "-") == 0) { + fp = stdin; + } else { + fp = fopen(file, "r"); + if(fp == NULL) { + fprintf(stderr, "Failed to read file: %s\n", file); + exit(3); + } + } + + while((nread = fread(fbuf, 1, 16384, fp)) > 0) { + if(buf == NULL) { + buflen = nread; + buf = (char*) malloc(nread + 1); + if(buf == NULL) { + fprintf(stderr, "Out of memory.\n"); + exit(3); + } + memcpy(buf, fbuf, buflen); + buf[buflen] = '\0'; + } else { + buflen = strlen(buf); + tmp = (char*) malloc(buflen + nread + 1); + if(tmp == NULL) { + fprintf(stderr, "Out of memory.\n"); + exit(3); + } + memcpy(tmp, buf, buflen); + memcpy(tmp+buflen, fbuf, nread); + tmp[buflen+nread] = '\0'; + free(buf); + buf = tmp; + } + } + return buf; +} + +couch_args* +couch_parse_args(int argc, const char* argv[]) +{ + couch_args* args; + int i = 1; + + args = (couch_args*) malloc(sizeof(couch_args)); + if(args == NULL) + return NULL; + + memset(args, '\0', sizeof(couch_args)); + args->stack_size = 8L * 1024L; + + while(i < argc) { + if(strcmp("--http", argv[i]) == 0) { + args->use_http = 1; + } else if(strcmp("--stack-size", argv[i]) == 0) { + args->stack_size = atoi(argv[i+1]); + if(args->stack_size <= 0) { + fprintf(stderr, "Invalid stack size.\n"); + exit(2); + } + } else { + args->script = slurp_file(args->script, argv[i]); + if(args->script_name == NULL) { + if(strcmp(argv[i], "-") == 0) { + args->script_name = ""; + } else { + args->script_name = argv[i]; + } + } else { + args->script_name = ""; + } + } + i++; + } + + if(args->script_name == NULL || args->script == NULL) { + fprintf(stderr, "No script provided.\n"); + exit(3); + } + + return args; +} + + +int +couch_fgets(char* buf, int size, FILE* fp) +{ + int n, i, c; + + if(size <= 0) return -1; + n = size - 1; + + for(i = 0; i < n && (c = getc(fp)) != EOF; i++) { + buf[i] = c; + if(c == '\n') { + i++; + break; + } + } + + buf[i] = '\0'; + return i; +} + + +JSString* +couch_readline(JSContext* cx, FILE* fp) +{ + JSString* str; + char* bytes = NULL; + char* tmp = NULL; + size_t used = 0; + size_t byteslen = 256; + size_t readlen = 0; + + bytes = JS_malloc(cx, byteslen); + if(bytes == NULL) return NULL; + + while((readlen = couch_fgets(bytes+used, byteslen-used, fp)) > 0) { + used += readlen; + + if(bytes[used-1] == '\n') { + bytes[used-1] = '\0'; + break; + } + + // Double our buffer and read more. + byteslen *= 2; + tmp = JS_realloc(cx, bytes, byteslen); + if(!tmp) { + JS_free(cx, bytes); + return NULL; + } + + bytes = tmp; + } + + // Treat empty strings specially + if(used == 0) { + JS_free(cx, bytes); + return JSVAL_TO_STRING(JS_GetEmptyStringValue(cx)); + } + + // Shring the buffer to the actual data size + tmp = JS_realloc(cx, bytes, used); + if(!tmp) { + JS_free(cx, bytes); + return NULL; + } + bytes = tmp; + byteslen = used; + + str = dec_string(cx, bytes, byteslen); + JS_free(cx, bytes); + return str; +} + + +JSObject* +couch_readfile(JSContext* cx, FILE* fp) +{ + return NULL; +} + + +void +couch_print(JSContext* cx, uintN argc, jsval* argv) +{ + char *bytes; + uintN i; + + for(i = 0; i < argc; i++) + { + bytes = enc_string(cx, argv[i], NULL); + if(!bytes) return; + + fprintf(stdout, "%s%s", i ? " " : "", bytes); + JS_free(cx, bytes); + } + + fputc('\n', stdout); + fflush(stdout); +} + + +void +couch_error(JSContext* cx, const char* mesg, JSErrorReport* report) +{ + if(!report || !JSREPORT_IS_WARNING(report->flags)) + { + fprintf(stderr, "[couchjs] %s\n", mesg); + } +} + + +JSBool +couch_load_funcs(JSContext* cx, JSObject* obj, JSFunctionSpec* funcs) +{ + JSFunctionSpec* f; + for(f = funcs; f->name != NULL; f++) { + if(!JS_DefineFunction(cx, obj, f->name, f->call, f->nargs, f->flags)) { + fprintf(stderr, "Failed to create function: %s\n", f->name); + return JS_FALSE; + } + } + return JS_TRUE; +} + diff --git a/src/couchdb/priv/couch_js/util.h b/src/couchdb/priv/couch_js/util.h new file mode 100644 index 00000000..6caebfa1 --- /dev/null +++ b/src/couchdb/priv/couch_js/util.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef COUCHJS_UTIL_H +#define COUCHJS_UTIL_H + +#include + +typedef struct { + int use_http; + int stack_size; + const char* script_name; + char* script; +} couch_args; + +void couch_usage(); +couch_args* couch_parse_args(int argc, const char* argv[]); +int couch_fgets(char* buf, int size, FILE* fp); +JSString* couch_readline(JSContext* cx, FILE* fp); +void couch_print(JSContext* cx, uintN argc, jsval* argv); +void couch_error(JSContext* cx, const char* mesg, JSErrorReport* report); +JSBool couch_load_funcs(JSContext* cx, JSObject* obj, JSFunctionSpec* funcs); + + +#endif // Included util.h -- cgit v1.2.3