Skip to content

Commit

Permalink
MB-17567 Add startup snapshot for builtin JavaScript functions
Browse files Browse the repository at this point in the history
Change-Id: I250172a9414bc75e818bc852c87032c93f602dc0
Reviewed-on: http://review.couchbase.org/62053
Reviewed-by: Dave Rigby <daver@couchbase.com>
Tested-by: buildbot <build@couchbase.com>
Reviewed-by: Volker Mische <volker.mische@gmail.com>
  • Loading branch information
hsharsha committed Apr 1, 2016
1 parent 1d86ff3 commit a75e732
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 74 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Expand Up @@ -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
Expand Down Expand Up @@ -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()
10 changes: 10 additions & 0 deletions 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}")
70 changes: 70 additions & 0 deletions 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;
};
50 changes: 50 additions & 0 deletions 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 <stdio.h>
#include <stdlib.h>

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;
}
22 changes: 22 additions & 0 deletions 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
79 changes: 5 additions & 74 deletions src/views/mapreduce/mapreduce.cc
Expand Up @@ -23,6 +23,7 @@
#include <stdlib.h>
// This is libv8_libplatform library which handles garbage collection for v8
#include <include/libplatform/libplatform.h>
#include "jsfunctions/jsfunctions_data.h"

using namespace v8;

Expand All @@ -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<Context> createJsContext();
static void emit(const FunctionCallbackInfo<Value> &args);

Expand All @@ -112,19 +52,22 @@ static void freeJsonListEntries(json_results_list_t &list);
static inline Handle<Array> 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()
{
V8::Dispose();
V8::ShutdownPlatform();
delete v8platform;
delete[] startupData.data;
}

void initContext(mapreduce_ctx_t *ctx,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -239,19 +183,6 @@ static Local<Context> createJsContext()
Handle<Context> context = Context::New(isolate, NULL, global);
Context::Scope context_scope(context);

Handle<Function> sumFun = compileFunction(SUM_FUNCTION_STRING);
context->Global()->Set(createUtf8String(isolate, "sum"), sumFun);

Handle<Function> decodeBase64Fun =
compileFunction(BASE64_FUNCTION_STRING);
context->Global()->Set(createUtf8String(isolate, "decodeBase64"),
decodeBase64Fun);

Handle<Function> 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.
Expand Down

0 comments on commit a75e732

Please sign in to comment.