diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b2d60fd..1b6330e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ SET(COUCHSTORE_SOURCES src/arena.cc src/bitfield.c src/btree_modify.cc src/views/file_merger.c src/views/file_sorter.c src/views/index_header.c src/views/keys.c src/views/mapreduce/mapreduce.cc + ${CMAKE_CURRENT_BINARY_DIR}/src/views/mapreduce/jsfunctions/jsfunctions_data.cc src/views/mapreduce/mapreduce_c.cc src/views/reducers.c src/views/reductions.c src/views/sorted_list.c src/views/spatial.c src/views/spatial_modify.c @@ -265,5 +266,6 @@ TARGET_LINK_LIBRARIES(couchstore_wrapped_fileops_test ADD_TEST(couchstore-wrapped_fileops-test couchstore_wrapped_fileops_test) ADD_SUBDIRECTORY(programs) +ADD_SUBDIRECTORY(src/views/mapreduce/jsfunctions) ENABLE_CODE_COVERAGE_REPORT() diff --git a/src/views/mapreduce/jsfunctions/CMakeLists.txt b/src/views/mapreduce/jsfunctions/CMakeLists.txt new file mode 100644 index 00000000..b4408d15 --- /dev/null +++ b/src/views/mapreduce/jsfunctions/CMakeLists.txt @@ -0,0 +1,10 @@ +SET(BUILTIN_JS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/builtin.js) + +TRY_RUN(EMBED_DATA_EXITCODE EMBED_DATA_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/embed_data.c + RUN_OUTPUT_VARIABLE JSFUNCTION_CONTENTS + ARGS ${BUILTIN_JS_FILE}) + +STRING(REPLACE "\r" "\n" JSFUNCTION_CONTENTS "${JSFUNCTION_CONTENTS}") +FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/jsfunctions_data.cc "${JSFUNCTION_CONTENTS}") diff --git a/src/views/mapreduce/jsfunctions/builtin.js b/src/views/mapreduce/jsfunctions/builtin.js new file mode 100644 index 00000000..a9122f89 --- /dev/null +++ b/src/views/mapreduce/jsfunctions/builtin.js @@ -0,0 +1,70 @@ +/** + * @copyright 2016 Couchbase, Inc. + * + * 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. + **/ + +function sum(values) { + var sum = 0; + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return sum; +}; + +// I wish it was on the prototype, but that will require bigger +// C changes as adding to the date prototype should be done on +// process launch. The code you see here may be faster, but it +// is less JavaScripty. +// "Date.prototype.toArray = (function() {" +function dateToArray(date) { + date = date.getUTCDate ? date : new Date(date); + return isFinite(date.valueOf()) ? + [date.getUTCFullYear(), + (date.getUTCMonth() + 1), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds()] : null; +}; + +function decodeBase64(b64) { + var i, j, l, tmp, scratch, arr = []; + var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + if (typeof b64 !== 'string') { + throw 'Input is not a string'; + } + if (b64.length % 4 > 0) { + throw 'Invalid base64 source.'; + } + scratch = b64.indexOf('='); + scratch = scratch > 0 ? b64.length - scratch : 0; + l = scratch > 0 ? b64.length - 4 : b64.length; + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12); + tmp |= (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]); + arr.push((tmp & 0xFF0000) >> 16); + arr.push((tmp & 0xFF00) >> 8); + arr.push(tmp & 0xFF); + } + if (scratch === 2) { + tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4); + arr.push(tmp & 0xFF); + } else if (scratch === 1) { + tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4); + tmp |= (lookup.indexOf(b64[i + 2]) >> 2); + arr.push((tmp >> 8) & 0xFF); + arr.push(tmp & 0xFF); + } + return arr; +}; diff --git a/src/views/mapreduce/jsfunctions/embed_data.c b/src/views/mapreduce/jsfunctions/embed_data.c new file mode 100644 index 00000000..2a44acfa --- /dev/null +++ b/src/views/mapreduce/jsfunctions/embed_data.c @@ -0,0 +1,50 @@ +/** + * @copyright 2016 Couchbase, Inc. + * + * 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. + **/ + +/* This code takes in list of file names in argv and embed the contents + * into a const char array. + **/ + +#include +#include + +int main(int argc, char **argv) +{ + FILE *fp; + int i, j, ch; + + printf("extern const unsigned char jsFunction_src[] = {"); + for(i = 1; i < argc; i++) { + if ((fp = fopen(argv[i], "rb")) == NULL) { + exit(EXIT_FAILURE); + } + else { + for (j = 0; (ch = fgetc(fp)) != EOF; j++) { + if ((j % 12) == 0) { + printf("%c", '\n'); + } + printf(" %#02x,", ch); + } + fclose(fp); + } + } + + // Append zero byte at the end, to make text files appear in memory + // as nul-terminated strings. + printf("%s", " 0x00\n};\n"); + + return EXIT_SUCCESS; +} diff --git a/src/views/mapreduce/jsfunctions/jsfunctions_data.h b/src/views/mapreduce/jsfunctions/jsfunctions_data.h new file mode 100644 index 00000000..726fa064 --- /dev/null +++ b/src/views/mapreduce/jsfunctions/jsfunctions_data.h @@ -0,0 +1,22 @@ +/** + * @copyright 2016 Couchbase, Inc. + * + * 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 _JSFUNCTION_DATA_H +#define _JSFUNCTION_DATA_H + +extern const unsigned char jsFunction_src[]; + +#endif //_JSFUNCTION_DATA_H diff --git a/src/views/mapreduce/mapreduce.cc b/src/views/mapreduce/mapreduce.cc index c0ca5010..bc2bdb42 100644 --- a/src/views/mapreduce/mapreduce.cc +++ b/src/views/mapreduce/mapreduce.cc @@ -23,6 +23,7 @@ #include // This is libv8_libplatform library which handles garbage collection for v8 #include +#include "jsfunctions/jsfunctions_data.h" using namespace v8; @@ -33,67 +34,6 @@ typedef struct { mapreduce_ctx_t *ctx; } isolate_data_t; - -static const char *SUM_FUNCTION_STRING = - "(function(values) {" - " var sum = 0;" - " for (var i = 0; i < values.length; ++i) {" - " sum += values[i];" - " }" - " return sum;" - "})"; - -static const char *DATE_FUNCTION_STRING = - // I wish it was on the prototype, but that will require bigger - // C changes as adding to the date prototype should be done on - // process launch. The code you see here may be faster, but it - // is less JavaScripty. - // "Date.prototype.toArray = (function() {" - "(function(date) {" - " date = date.getUTCDate ? date : new Date(date);" - " return isFinite(date.valueOf()) ?" - " [date.getUTCFullYear()," - " (date.getUTCMonth() + 1)," - " date.getUTCDate()," - " date.getUTCHours()," - " date.getUTCMinutes()," - " date.getUTCSeconds()] : null;" - "})"; - -static const char *BASE64_FUNCTION_STRING = - "(function(b64) {" - " var i, j, l, tmp, scratch, arr = [];" - " var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';" - " if (typeof b64 !== 'string') {" - " throw 'Input is not a string';" - " }" - " if (b64.length % 4 > 0) {" - " throw 'Invalid base64 source.';" - " }" - " scratch = b64.indexOf('=');" - " scratch = scratch > 0 ? b64.length - scratch : 0;" - " l = scratch > 0 ? b64.length - 4 : b64.length;" - " for (i = 0, j = 0; i < l; i += 4, j += 3) {" - " tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12);" - " tmp |= (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]);" - " arr.push((tmp & 0xFF0000) >> 16);" - " arr.push((tmp & 0xFF00) >> 8);" - " arr.push(tmp & 0xFF);" - " }" - " if (scratch === 2) {" - " tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4);" - " arr.push(tmp & 0xFF);" - " } else if (scratch === 1) {" - " tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4);" - " tmp |= (lookup.indexOf(b64[i + 2]) >> 2);" - " arr.push((tmp >> 8) & 0xFF);" - " arr.push(tmp & 0xFF);" - " }" - " return arr;" - "})"; - - - static Local createJsContext(); static void emit(const FunctionCallbackInfo &args); @@ -112,12 +52,14 @@ static void freeJsonListEntries(json_results_list_t &list); static inline Handle jsonListToJsArray(const mapreduce_json_list_t &list); static Platform *v8platform; +static StartupData startupData; void initV8() { V8::InitializeICU(); v8platform = platform::CreateDefaultPlatform(); V8::InitializePlatform(v8platform); V8::Initialize(); + startupData = V8::CreateSnapshotDataBlob((char *)jsFunction_src); } void deinitV8() @@ -125,6 +67,7 @@ void deinitV8() V8::Dispose(); V8::ShutdownPlatform(); delete v8platform; + delete[] startupData.data; } void initContext(mapreduce_ctx_t *ctx, @@ -196,6 +139,7 @@ static void doInitContext(mapreduce_ctx_t *ctx) { ctx->bufAllocator = new ArrayBufferAllocator(); Isolate::CreateParams createParams; + createParams.snapshot_blob = &startupData; createParams.array_buffer_allocator = ctx->bufAllocator; ctx->isolate = Isolate::New(createParams); Locker locker(ctx->isolate); @@ -239,19 +183,6 @@ static Local createJsContext() Handle context = Context::New(isolate, NULL, global); Context::Scope context_scope(context); - Handle sumFun = compileFunction(SUM_FUNCTION_STRING); - context->Global()->Set(createUtf8String(isolate, "sum"), sumFun); - - Handle decodeBase64Fun = - compileFunction(BASE64_FUNCTION_STRING); - context->Global()->Set(createUtf8String(isolate, "decodeBase64"), - decodeBase64Fun); - - Handle dateToArrayFun = - compileFunction(DATE_FUNCTION_STRING); - context->Global()->Set(createUtf8String(isolate, "dateToArray"), - dateToArrayFun); - // Use EscapableHandleScope and return using .Escape // This will ensure that return values are not garbage collected // as soon as the function returns.