diff options
Diffstat (limited to 'couchjs')
-rw-r--r-- | couchjs/c_src/SConscript | 54 | ||||
-rw-r--r-- | couchjs/c_src/http.c | 382 | ||||
-rw-r--r-- | couchjs/c_src/http.h | 12 | ||||
-rw-r--r-- | couchjs/c_src/main.c | 334 | ||||
-rw-r--r-- | couchjs/c_src/sm.h | 13 | ||||
-rw-r--r-- | couchjs/c_src/sm170.c | 378 | ||||
-rw-r--r-- | couchjs/c_src/sm180.c | 387 | ||||
-rw-r--r-- | couchjs/c_src/sm185.c | 401 | ||||
-rw-r--r-- | couchjs/c_src/utf8.c | 18 | ||||
-rw-r--r-- | couchjs/c_src/util.c | 235 | ||||
-rw-r--r-- | couchjs/c_src/util.h | 34 | ||||
-rw-r--r-- | couchjs/js/mimeparse.js | 2 | ||||
-rw-r--r-- | couchjs/js/render.js | 10 | ||||
-rw-r--r-- | couchjs/js/util.js | 11 |
14 files changed, 1690 insertions, 581 deletions
diff --git a/couchjs/c_src/SConscript b/couchjs/c_src/SConscript index cfce5605..b014e2ec 100644 --- a/couchjs/c_src/SConscript +++ b/couchjs/c_src/SConscript @@ -19,7 +19,10 @@ def require_lib(name): print 'Could not find required library', name Exit(1) -env = Environment(CCFLAGS='-g -O2 -DXP_UNIX') +def runcmd(cmd): + return commands.getstatusoutput(cmd) + +env = Environment(CC="c++", CCFLAGS='-g -O2 -DXP_UNIX') if os.uname()[0] == 'SunOS': env['CC'] = '/usr/sfw/bin/gcc' @@ -33,12 +36,15 @@ if os.uname()[0] == 'FreeBSD': env['LIB_COMPAT'] = 'compat' if os.path.exists('/usr/bin/pkg-config'): - (status, output) = commands.getstatusoutput("/usr/bin/pkg-config mozilla-js --cflags") - if status == 0: - env['CCFLAGS'] += output - (status, output) = commands.getstatusoutput("/usr/bin/pkg-config mozilla-js --libs-only-L") - if status == 0: - env.Append(LINKFLAGS=output) + for pkg in ["mozilla-js-185", "mozilla-js"]: + (s1, output) = runcmd("/usr/bin/pkg-config %s --cflags" % pkg) + if s1 == 0: + env.Append(CCFLAGS=" " + output) + (s2, output) = runcmd("/usr/bin/pkg-config %s --libs-only-L" % pkg) + if s2 == 0: + env.Append(LINKFLAGS=output) + if s1 == 0 or s2 == 0: + break if not env.GetOption('clean'): conf = Configure(env, config_h='config.h') @@ -46,12 +52,13 @@ if not env.GetOption('clean'): require_lib('m') require_lib('pthread') require_lib('curl') + require_lib('nspr4') ## check for SpiderMonkey development header - if conf.CheckHeader('js/jsapi.h'): - jsapi = 'js/jsapi.h' - elif conf.CheckHeader('mozjs/jsapi.h'): + if conf.CheckHeader('mozjs/jsapi.h'): jsapi = 'mozjs/jsapi.h' + elif conf.CheckHeader('js/jsapi.h'): + jsapi = 'js/jsapi.h' elif conf.CheckHeader('jsapi.h'): jsapi = 'jsapi.h' else: @@ -60,16 +67,25 @@ if not env.GetOption('clean'): Exit(1) ## check for SpiderMonkey library as libjs or libmozjs - if not conf.CheckLibWithHeader('mozjs', jsapi, 'c', autoadd=1): - if not conf.CheckLibWithHeader('js', jsapi, 'c', autoadd=1): - print 'Could not find JS library.', \ - 'Is Mozilla SpiderMonkey installed?' - Exit(1) + if not conf.CheckLibWithHeader('mozjs185-1.0', jsapi, 'c', autoadd=1): + if not conf.CheckLibWithHeader('mozjs', jsapi, 'c', autoadd=1): + if not conf.CheckLibWithHeader('js', jsapi, 'c', autoadd=1): + print 'Could not find JS library.', \ + 'Is Mozilla SpiderMonkey installed?' + Exit(1) - ## SpiderMonkey 1.8 has this callback we use for memory management - if conf.CheckDeclaration('JS_SetOperationCallback', '#include <%s>' % jsapi): - conf.Define('USE_JS_SETOPCB') + ## Detect the version of SpiderMonkey we're using + jsheader = "#include <%s>" % jsapi + versions = [ + ("JS_NewCompartmentAndGlobalObject", "SM185"), + ("JS_ThrowStopIteration", "SM180"), + ("JS_GetStringCharsAndLength", "SM170") + ] + for func, vsn in versions: + if conf.CheckDeclaration(func, jsheader): + conf.Define(vsn) + break env = conf.Finish() -env.Program('couchjs', ['main.c', 'http.c', 'utf8.c']) +env.Program('couchjs', ['main.c', 'http.c', 'utf8.c', 'util.c']) diff --git a/couchjs/c_src/http.c b/couchjs/c_src/http.c index b781f0ef..aa21515c 100644 --- a/couchjs/c_src/http.c +++ b/couchjs/c_src/http.c @@ -13,29 +13,87 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> - #include "config.h" -#ifdef HAVE_JS_JSAPI_H -#include <js/jsapi.h> -#elif HAVE_MOZJS_JSAPI_H -#include <mozjs/jsapi.h> -#else -#include <jsapi.h> -#endif +#include "sm.h" +#include "utf8.h" + +// Soft dependency on cURL bindings because they're +// only used when running the JS tests from the +// command line which is rare. +#ifndef HAVE_LIBCURL + +void +http_check_enabled() +{ + fprintf(stderr, "HTTP API was disabled at compile time.\n"); + exit(3); +} + + +JSBool +http_ctor(JSContext* cx, JSObject* req) +{ + return JS_FALSE; +} + + +JSBool +http_dtor(JSContext* cx, JSObject* req) +{ + return JS_FALSE; +} + + +JSBool +http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) +{ + return JS_FALSE; +} + + +JSBool +http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) +{ + return JS_FALSE; +} + + +JSBool +http_send(JSContext* cx, JSObject* req, jsval body) +{ + return JS_FALSE; +} + + +int +http_status(JSContext* cx, JSObject* req) +{ + return -1; +} + +#else #include <curl/curl.h> -#include "utf8.h" -#ifdef XP_WIN +void +http_check_enabled() +{ + return; +} + + // Map some of the string function names to things which exist on Windows +#ifdef XP_WIN #define strcasecmp _strcmpi #define strncasecmp _strnicmp #define snprintf _snprintf #endif + typedef struct curl_slist CurlHeaders; + typedef struct { int method; char* url; @@ -43,7 +101,9 @@ typedef struct { jsint last_status; } HTTPData; -char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; + +const char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; + #define GET 0 #define HEAD 1 @@ -52,14 +112,17 @@ char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; #define DELETE 4 #define COPY 5 + static JSBool go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen); + static JSString* str_from_binary(JSContext* cx, char* data, size_t length); -static JSBool -constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) + +JSBool +http_ctor(JSContext* cx, JSObject* req) { HTTPData* http = NULL; JSBool ret = JS_FALSE; @@ -76,7 +139,7 @@ constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) http->req_headers = NULL; http->last_status = -1; - if(!JS_SetPrivate(cx, obj, http)) + if(!JS_SetPrivate(cx, req, http)) { JS_ReportError(cx, "Failed to set private CouchHTTP data."); goto error; @@ -92,90 +155,76 @@ success: return ret; } -static void -destructor(JSContext* cx, JSObject* obj) + +void +http_dtor(JSContext* cx, JSObject* obj) { HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - if(!http) - { - fprintf(stderr, "Unable to destroy invalid CouchHTTP instance.\n"); - } - else - { + if(http) { if(http->url) free(http->url); if(http->req_headers) curl_slist_free_all(http->req_headers); free(http); } } -static JSBool -open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) + +JSBool +http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); char* method = NULL; - char* url = NULL; - JSBool ret = JS_FALSE; int methid; + JSBool ret = JS_FALSE; - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(argv[0] == JSVAL_VOID) - { + if(mth == JSVAL_VOID) { JS_ReportError(cx, "You must specify a method."); goto done; } - method = enc_string(cx, argv[0], NULL); - if(!method) - { + method = enc_string(cx, mth, NULL); + if(!method) { JS_ReportError(cx, "Failed to encode method."); goto done; } - for(methid = 0; METHODS[methid] != NULL; methid++) - { + for(methid = 0; METHODS[methid] != NULL; methid++) { if(strcasecmp(METHODS[methid], method) == 0) break; } - if(methid > COPY) - { + if(methid > COPY) { JS_ReportError(cx, "Invalid method specified."); goto done; } http->method = methid; - if(argv[1] == JSVAL_VOID) - { + if(url == JSVAL_VOID) { JS_ReportError(cx, "You must specify a URL."); goto done; } - if(http->url) - { + if(http->url != NULL) { free(http->url); http->url = NULL; } - http->url = enc_string(cx, argv[1], NULL); - if(!http->url) - { + http->url = enc_string(cx, url, NULL); + if(http->url == NULL) { JS_ReportError(cx, "Failed to encode URL."); goto done; } - if(argv[2] != JSVAL_VOID && argv[2] != JSVAL_FALSE) - { - JS_ReportError(cx, "Synchronous flag must be false if specified."); + if(snc != JSVAL_FALSE) { + JS_ReportError(cx, "Synchronous flag must be false."); goto done; } - if(http->req_headers) - { + if(http->req_headers) { curl_slist_free_all(http->req_headers); http->req_headers = NULL; } @@ -190,42 +239,42 @@ done: return ret; } -static JSBool -setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) + +JSBool +http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); char* keystr = NULL; char* valstr = NULL; char* hdrbuf = NULL; size_t hdrlen = -1; JSBool ret = JS_FALSE; - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(argv[0] == JSVAL_VOID) + if(name == JSVAL_VOID) { JS_ReportError(cx, "You must speciy a header name."); goto done; } - keystr = enc_string(cx, argv[0], NULL); + keystr = enc_string(cx, name, NULL); if(!keystr) { JS_ReportError(cx, "Failed to encode header name."); goto done; } - if(argv[1] == JSVAL_VOID) + if(val == JSVAL_VOID) { JS_ReportError(cx, "You must specify a header value."); goto done; } - valstr = enc_string(cx, argv[1], NULL); + valstr = enc_string(cx, val, NULL); if(!valstr) { JS_ReportError(cx, "Failed to encode header value."); @@ -234,8 +283,7 @@ setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) hdrlen = strlen(keystr) + strlen(valstr) + 3; hdrbuf = (char*) malloc(hdrlen * sizeof(char)); - if(!hdrbuf) - { + if(!hdrbuf) { JS_ReportError(cx, "Failed to allocate header buffer."); goto done; } @@ -249,121 +297,50 @@ done: if(keystr) free(keystr); if(valstr) free(valstr); if(hdrbuf) free(hdrbuf); - return ret; } -static JSBool -sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +JSBool +http_send(JSContext* cx, JSObject* req, jsval body) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - char* body = NULL; + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); + char* bodystr = NULL; size_t bodylen = 0; JSBool ret = JS_FALSE; - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx)) - { - body = enc_string(cx, argv[0], &bodylen); - if(!body) - { + if(body != JSVAL_VOID && body != JS_GetEmptyStringValue(cx)) { + bodystr = enc_string(cx, body, &bodylen); + if(!bodystr) { JS_ReportError(cx, "Failed to encode body."); goto done; } } - ret = go(cx, obj, http, body, bodylen); + ret = go(cx, req, http, bodystr, bodylen); done: - if(body) free(body); + if(bodystr) free(bodystr); return ret; } -static JSBool -status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) +int +http_status(JSContext* cx, JSObject* req) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); return JS_FALSE; } - if(INT_FITS_IN_JSVAL(http->last_status)) - { - *vp = INT_TO_JSVAL(http->last_status); - return JS_TRUE; - } - else - { - JS_ReportError(cx, "INTERNAL: Invalid last_status"); - return JS_FALSE; - } -} - -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, - destructor, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -JSPropertySpec CouchHTTPProperties[] = { - {"status", 0, JSPROP_READONLY, status, NULL}, - {0, 0, 0, 0, 0} -}; - -JSFunctionSpec CouchHTTPFunctions[] = { - {"_open", open, 3, 0, 0}, - {"_setRequestHeader", setheader, 2, 0, 0}, - {"_send", sendreq, 1, 0, 0}, - {0, 0, 0, 0, 0} -}; - -JSObject* -install_http(JSContext* cx, JSObject* glbl) -{ - JSObject* klass = NULL; - HTTPData* http = NULL; - - klass = JS_InitClass( - cx, - glbl, - NULL, - &CouchHTTPClass, - constructor, - 0, - CouchHTTPProperties, - CouchHTTPFunctions, - NULL, - NULL - ); - - if(!klass) - { - fprintf(stderr, "Failed to initialize CouchHTTP class.\n"); - return NULL; - } - - return klass; + return http->last_status; } - // Curl Helpers typedef struct { @@ -373,6 +350,7 @@ typedef struct { char* sendbuf; size_t sendlen; size_t sent; + int sent_once; char* recvbuf; size_t recvlen; size_t read; @@ -404,13 +382,13 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) state.sendbuf = body; state.sendlen = bodylen; state.sent = 0; + state.sent_once = 0; state.recvbuf = NULL; state.recvlen = 0; state.read = 0; - if(HTTP_HANDLE == NULL) - { + if(HTTP_HANDLE == NULL) { HTTP_HANDLE = curl_easy_init(); curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body); curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION, @@ -425,14 +403,12 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) "CouchHTTP Client - Relax"); } - if(!HTTP_HANDLE) - { + if(!HTTP_HANDLE) { JS_ReportError(cx, "Failed to initialize cURL handle."); goto done; } - if(http->method < 0 || http->method > COPY) - { + if(http->method < 0 || http->method > COPY) { JS_ReportError(cx, "INTERNAL: Unknown method."); goto done; } @@ -442,27 +418,21 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0); - if(http->method == HEAD) - { + if(http->method == HEAD) { curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0); - } - else if(http->method == POST || http->method == PUT) - { + } else if(http->method == POST || http->method == PUT) { curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0); } - if(body && bodylen) - { + if(body && bodylen) { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen); - } - else - { + } else { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0); } - //curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1); + // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url); curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers); @@ -471,39 +441,32 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state); curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state); - if(curl_easy_perform(HTTP_HANDLE) != 0) - { + if(curl_easy_perform(HTTP_HANDLE) != 0) { JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF); goto done; } - if(!state.resp_headers) - { + if(!state.resp_headers) { JS_ReportError(cx, "Failed to recieve HTTP headers."); goto done; } tmp = OBJECT_TO_JSVAL(state.resp_headers); if(!JS_DefineProperty( - cx, - obj, + cx, obj, "_headers", tmp, - NULL, - NULL, + NULL, NULL, JSPROP_READONLY - )) - { + )) { JS_ReportError(cx, "INTERNAL: Failed to set response headers."); goto done; } - if(state.recvbuf) // Is good enough? - { + if(state.recvbuf) { state.recvbuf[state.read] = '\0'; jsbody = dec_string(cx, state.recvbuf, state.read+1); - if(!jsbody) - { + if(!jsbody) { // If we can't decode the body as UTF-8 we forcefully // convert it to a string by just forcing each byte // to a jschar. @@ -516,22 +479,17 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) } } tmp = STRING_TO_JSVAL(jsbody); - } - else - { + } else { tmp = JS_GetEmptyStringValue(cx); } if(!JS_DefineProperty( - cx, - obj, + cx, obj, "responseText", tmp, - NULL, - NULL, + NULL, NULL, JSPROP_READONLY - )) - { + )) { JS_ReportError(cx, "INTERNAL: Failed to set responseText."); goto done; } @@ -549,15 +507,20 @@ send_body(void *ptr, size_t size, size_t nmem, void *data) CurlState* state = (CurlState*) data; size_t length = size * nmem; size_t towrite = state->sendlen - state->sent; - if(towrite == 0) - { + + // Assume this is cURL trying to resend a request that + // failed. + if(towrite == 0 && state->sent_once == 0) { + state->sent_once = 1; return 0; + } else if(towrite == 0) { + state->sent = 0; + state->sent_once = 0; + towrite = state->sendlen; } if(length < towrite) towrite = length; - //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length, towrite); - memcpy(ptr, state->sendbuf + state->sent, towrite); state->sent += towrite; @@ -581,15 +544,12 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) char code[4]; char* header = (char*) ptr; size_t length = size * nmem; - size_t index = 0; JSString* hdr = NULL; jsuint hdrlen; jsval hdrval; - if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) - { - if(length < 12) - { + if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) { + if(length < 12) { return CURLE_WRITE_ERROR; } @@ -598,8 +558,7 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) state->http->last_status = atoi(code); state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL); - if(!state->resp_headers) - { + if(!state->resp_headers) { return CURLE_WRITE_ERROR; } @@ -607,26 +566,22 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) } // We get a notice at the \r\n\r\n after headers. - if(length <= 2) - { + if(length <= 2) { return length; } // Append the new header to our array. hdr = dec_string(state->cx, header, length); - if(!hdr) - { + if(!hdr) { return CURLE_WRITE_ERROR; } - if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) - { + if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) { return CURLE_WRITE_ERROR; } hdrval = STRING_TO_JSVAL(hdr); - if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) - { + if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) { return CURLE_WRITE_ERROR; } @@ -640,21 +595,19 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data) size_t length = size * nmem; char* tmp = NULL; - if(!state->recvbuf) - { + if(!state->recvbuf) { state->recvlen = 4096; state->read = 0; - state->recvbuf = JS_malloc(state->cx, state->recvlen); + state->recvbuf = (char*) JS_malloc(state->cx, state->recvlen); } - if(!state->recvbuf) - { + if(!state->recvbuf) { return CURLE_WRITE_ERROR; } // +1 so we can add '\0' back up in the go function. while(length+1 > state->recvlen - state->read) state->recvlen *= 2; - tmp = JS_realloc(state->cx, state->recvbuf, state->recvlen); + tmp = (char*) JS_realloc(state->cx, state->recvbuf, state->recvlen); if(!tmp) return CURLE_WRITE_ERROR; state->recvbuf = tmp; @@ -672,8 +625,7 @@ str_from_binary(JSContext* cx, char* data, size_t length) if(!conv) return NULL; - for(i = 0; i < length; i++) - { + for(i = 0; i < length; i++) { conv[i] = (jschar) data[i]; } @@ -682,3 +634,5 @@ str_from_binary(JSContext* cx, char* data, size_t length) return ret; } + +#endif /* HAVE_CURL */ diff --git a/couchjs/c_src/http.h b/couchjs/c_src/http.h index b5f8c70f..75a0d561 100644 --- a/couchjs/c_src/http.h +++ b/couchjs/c_src/http.h @@ -13,6 +13,14 @@ #ifndef COUCH_JS_HTTP_H #define COUCH_JS_HTTP_H -JSObject* install_http(JSContext* cx, JSObject* global); +#include "sm.h" -#endif
\ No newline at end of file +void http_check_enabled(); +JSBool http_ctor(JSContext* cx, JSObject* req); +void http_dtor(JSContext* cx, JSObject* req); +JSBool http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc); +JSBool http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val); +JSBool http_send(JSContext* cx, JSObject* req, jsval body); +int http_status(JSContext* cx, JSObject* req); + +#endif diff --git a/couchjs/c_src/main.c b/couchjs/c_src/main.c index 25acaf55..209bb023 100644 --- a/couchjs/c_src/main.c +++ b/couchjs/c_src/main.c @@ -10,336 +10,12 @@ // License for the specific language governing permissions and limitations under // the License. -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - #include "config.h" -#ifdef HAVE_JS_JSAPI_H -#include <js/jsapi.h> -#elif HAVE_MOZJS_JSAPI_H -#include <mozjs/jsapi.h> -#else -#include <jsapi.h> -#endif - -#include "utf8.h" -#include "http.h" -int gExitCode = 0; - -#ifdef JS_THREADSAFE -#define SETUP_REQUEST(cx) \ - JS_SetContextThread(cx); \ - JS_BeginRequest(cx); -#define FINISH_REQUEST(cx) \ - JS_EndRequest(cx); \ - JS_ClearContextThread(cx); +#if defined(SM185) +#include "sm185.c" +#elif defined(SM180) +#include "sm180.c" #else -#define SETUP_REQUEST(cx) -#define FINISH_REQUEST(cx) +#include "sm170.c" #endif - -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; - jsval v; - - 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); - return JS_TRUE; -} - -static JSBool -print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i; - char *bytes; - - for(i = 0; i < argc; i++) - { - bytes = enc_string(cx, argv[i], NULL); - if(!bytes) return JS_FALSE; - - fprintf(stdout, "%s%s", i ? " " : "", bytes); - JS_free(cx, bytes); - } - - fputc('\n', stdout); - fflush(stdout); - return JS_TRUE; -} - -static JSBool -quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JS_ConvertArguments(cx, argc, argv, "/ i", &gExitCode); - return JS_FALSE; -} - -static char* -readfp(JSContext* cx, FILE* fp, size_t* buflen) -{ - 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 = js_fgets(bytes+used, byteslen-used, stdin)) > 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; - } - - *buflen = used; - return bytes; -} - -static JSBool -readline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jschar *chars; - JSString *str; - char* bytes; - char* tmp; - size_t byteslen; - - /* GC Occasionally */ - JS_MaybeGC(cx); - - bytes = readfp(cx, stdin, &byteslen); - if(!bytes) return JS_FALSE; - - /* Treat the empty string specially */ - if(byteslen == 0) - { - *rval = JS_GetEmptyStringValue(cx); - JS_free(cx, bytes); - return JS_TRUE; - } - - /* Shrink the buffer to the real size */ - tmp = JS_realloc(cx, bytes, byteslen); - if(!tmp) - { - JS_free(cx, bytes); - return JS_FALSE; - } - bytes = tmp; - - str = dec_string(cx, bytes, byteslen); - JS_free(cx, bytes); - - if(!str) return JS_FALSE; - - *rval = STRING_TO_JSVAL(str); - - 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) - return JS_TRUE; - return JS_SealObject(cx, target, deep); -} - -static void -execute_script(JSContext *cx, JSObject *obj, const char *filename) { - FILE *file; - JSScript *script; - jsval result; - - if(!filename || strcmp(filename, "-") == 0) - { - file = stdin; - } - else - { - file = fopen(filename, "r"); - if (!file) - { - fprintf(stderr, "could not open script file %s\n", filename); - gExitCode = 1; - return; - } - } - - script = JS_CompileFileHandle(cx, obj, filename, file); - if(script) - { - JS_ExecuteScript(cx, obj, script, &result); - JS_DestroyScript(cx, script); - } -} - -static void -printerror(JSContext *cx, const char *mesg, JSErrorReport *report) -{ - if(!report || !JSREPORT_IS_WARNING(report->flags)) - { - fprintf(stderr, "%s\n", mesg); - } -} - -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} -}; - -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 -}; - -int -main(int argc, const char * argv[]) -{ - JSRuntime* rt = NULL; - JSContext* cx = NULL; - JSObject* global = NULL; - JSFunctionSpec* sp = NULL; - int i = 0; - - rt = JS_NewRuntime(64L * 1024L * 1024L); - if (!rt) return 1; - - cx = JS_NewContext(rt, 8L * 1024L); - if (!cx) return 1; - - JS_SetErrorReporter(cx, printerror); - JS_ToggleOptions(cx, JSOPTION_XML); - - SETUP_REQUEST(cx); - - global = JS_NewObject(cx, &global_class, NULL, NULL); - if (!global) return 1; - if (!JS_InitStandardClasses(cx, global)) return 1; - - for(sp = global_functions; sp->name != NULL; sp++) - { - if(!JS_DefineFunction(cx, global, - sp->name, sp->call, sp->nargs, sp->flags)) - { - fprintf(stderr, "Failed to create function: %s\n", sp->name); - return 1; - } - } - - if(!install_http(cx, global)) - { - return 1; - } - - JS_SetGlobalObject(cx, global); - - if(argc > 2) - { - fprintf(stderr, "incorrect number of arguments\n\n"); - fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]); - return 2; - } - - if(argc == 0) - { - execute_script(cx, global, NULL); - } - else - { - execute_script(cx, global, argv[1]); - } - - FINISH_REQUEST(cx); - - JS_DestroyContext(cx); - JS_DestroyRuntime(rt); - JS_ShutDown(); - - return gExitCode; -} diff --git a/couchjs/c_src/sm.h b/couchjs/c_src/sm.h new file mode 100644 index 00000000..a40e054d --- /dev/null +++ b/couchjs/c_src/sm.h @@ -0,0 +1,13 @@ +#ifndef COUCHJS_SM_H +#define COUCHJS_SM_H + +#include "config.h" +#ifdef HAVE_JS_JSAPI_H +#include <js/jsapi.h> +#elif HAVE_MOZJS_JSAPI_H +#include <mozjs/jsapi.h> +#else +#include <jsapi.h> +#endif + +#endif // included sm.h diff --git a/couchjs/c_src/sm170.c b/couchjs/c_src/sm170.c new file mode 100644 index 00000000..44cb5870 --- /dev/null +++ b/couchjs/c_src/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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#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; + + 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/couchjs/c_src/sm180.c b/couchjs/c_src/sm180.c new file mode 100644 index 00000000..7dc2a974 --- /dev/null +++ b/couchjs/c_src/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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "http.h" +#include "sm.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/couchjs/c_src/sm185.c b/couchjs/c_src/sm185.c new file mode 100644 index 00000000..9815f15c --- /dev/null +++ b/couchjs/c_src/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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "http.h" +#include "sm.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/couchjs/c_src/utf8.c b/couchjs/c_src/utf8.c index 57928ba9..ace6badb 100644 --- a/couchjs/c_src/utf8.c +++ b/couchjs/c_src/utf8.c @@ -11,13 +11,7 @@ // the License. #include "config.h" -#ifdef HAVE_JS_JSAPI_H -#include <js/jsapi.h> -#elif HAVE_MOZJS_JSAPI_H -#include <mozjs/jsapi.h> -#else -#include <jsapi.h> -#endif +#include "sm.h" static int enc_char(uint8 *utf8Buffer, uint32 ucs4Char) @@ -128,7 +122,7 @@ char* enc_string(JSContext* cx, jsval arg, size_t* buflen) { JSString* str = NULL; - jschar* src = NULL; + const jschar* src = NULL; char* bytes = NULL; size_t srclen = 0; size_t byteslen = 0; @@ -136,12 +130,16 @@ enc_string(JSContext* cx, jsval arg, size_t* buflen) str = JS_ValueToString(cx, arg); if(!str) goto error; +#ifdef SM185 + src = JS_GetStringCharsAndLength(cx, str, &srclen); +#else src = JS_GetStringChars(str); srclen = JS_GetStringLength(str); +#endif if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error; - bytes = JS_malloc(cx, (byteslen) + 1); + bytes = (char*) JS_malloc(cx, (byteslen) + 1); bytes[byteslen] = 0; if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error; @@ -273,7 +271,7 @@ dec_string(JSContext* cx, const char* bytes, size_t byteslen) if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) goto error; - chars = JS_malloc(cx, (charslen + 1) * sizeof(jschar)); + chars = (jschar*) JS_malloc(cx, (charslen + 1) * sizeof(jschar)); if(!chars) return NULL; chars[charslen] = 0; diff --git a/couchjs/c_src/util.c b/couchjs/c_src/util.c new file mode 100644 index 00000000..f5ef3e82 --- /dev/null +++ b/couchjs/c_src/util.c @@ -0,0 +1,235 @@ +// 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 <stdlib.h> +#include <string.h> + +#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 = "<stdin>"; + } else { + args->script_name = argv[i]; + } + } else { + args->script_name = "<multiple_files>"; + } + } + 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 = (char*) 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 = (char*) 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 = (char*) 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/couchjs/c_src/util.h b/couchjs/c_src/util.h new file mode 100644 index 00000000..54ccdaa5 --- /dev/null +++ b/couchjs/c_src/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 "sm.h" + +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 diff --git a/couchjs/js/mimeparse.js b/couchjs/js/mimeparse.js index 3642a194..42b600fa 100644 --- a/couchjs/js/mimeparse.js +++ b/couchjs/js/mimeparse.js @@ -97,7 +97,7 @@ var Mimeparse = (function() { if ((type == targetType || type == "*" || targetType == "*") && (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) { var matchCount = 0; - for (param in targetParams) { + for (var param in targetParams) { if (param != 'q' && params[param] && params[param] == targetParams[param]) { matchCount += 1; } diff --git a/couchjs/js/render.js b/couchjs/js/render.js index d207db41..93ff6332 100644 --- a/couchjs/js/render.js +++ b/couchjs/js/render.js @@ -220,10 +220,10 @@ var Render = (function() { resetList(); Mime.resetProvides(); var resp = fun.apply(ddoc, args) || {}; + resp = maybeWrapResponse(resp); // handle list() style API if (chunks.length && chunks.length > 0) { - resp = maybeWrapResponse(resp); resp.headers = resp.headers || {}; for(var header in startResp) { resp.headers[header] = startResp[header] @@ -233,8 +233,12 @@ var Render = (function() { } if (Mime.providesUsed) { - resp = Mime.runProvides(args[1], ddoc); - resp = applyContentType(maybeWrapResponse(resp), Mime.responseContentType); + var provided_resp = Mime.runProvides(args[1], ddoc) || {}; + provided_resp = maybeWrapResponse(provided_resp); + resp.body = (resp.body || "") + chunks.join(""); + resp.body += provided_resp.body || ""; + resp = applyContentType(resp, Mime.responseContentType); + resetList(); } var type = typeOf(resp); diff --git a/couchjs/js/util.js b/couchjs/js/util.js index e4386701..d498ee64 100644 --- a/couchjs/js/util.js +++ b/couchjs/js/util.js @@ -46,7 +46,7 @@ var resolveModule = function(names, mod, root) { } else if (root) { mod = {current : root}; } - if (!mod.current[n]) { + if (mod.current[n] === undefined) { throw ["error", "invalid_require_path", 'Object has no property "'+n+'". '+JSON.stringify(mod.current)]; } return resolveModule(names, { @@ -63,6 +63,11 @@ var Couch = { }, compileFunction : function(source, ddoc) { if (!source) throw(["error","not_found","missing function"]); + // Some newer SpiderMonkey's appear to not like evaluating + // an anonymous function at global scope. Simple fix just + // wraps the source with parens so the function object is + // returned correctly. + source = "(" + source + ")"; try { if (sandbox) { if (ddoc) { @@ -91,9 +96,9 @@ var Couch = { } sandbox.require = require; } - var functionObject = evalcx(source, sandbox); + var functionObject = evalcx("(" + source + ")", sandbox); } else { - var functionObject = eval(source); + var functionObject = eval("(" + source + ")"); } } catch (err) { throw(["error", "compilation_error", err.toSource() + " (" + source + ")"]); |