1282a9d8aSFilipe David Borba Manana/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2f08dea7dSFilipe David Borba Manana/**
3f08dea7dSFilipe David Borba Manana * @copyright 2012 Couchbase, Inc.
4f08dea7dSFilipe David Borba Manana *
5f08dea7dSFilipe David Borba Manana * @author Filipe Manana  <filipe@couchbase.com>
6f08dea7dSFilipe David Borba Manana *
7f08dea7dSFilipe David Borba Manana * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8f08dea7dSFilipe David Borba Manana * use this file except in compliance with the License. You may obtain a copy of
9f08dea7dSFilipe David Borba Manana * the License at
10f08dea7dSFilipe David Borba Manana *
11f08dea7dSFilipe David Borba Manana *  http://www.apache.org/licenses/LICENSE-2.0
12f08dea7dSFilipe David Borba Manana *
13f08dea7dSFilipe David Borba Manana * Unless required by applicable law or agreed to in writing, software
14f08dea7dSFilipe David Borba Manana * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15f08dea7dSFilipe David Borba Manana * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16f08dea7dSFilipe David Borba Manana * License for the specific language governing permissions and limitations under
17f08dea7dSFilipe David Borba Manana * the License.
18f08dea7dSFilipe David Borba Manana **/
19f08dea7dSFilipe David Borba Manana
20f08dea7dSFilipe David Borba Manana#include <iostream>
21f08dea7dSFilipe David Borba Manana#include <list>
22f08dea7dSFilipe David Borba Manana#include <vector>
23f08dea7dSFilipe David Borba Manana#include <string>
24f08dea7dSFilipe David Borba Manana#include <string.h>
255297f10dSFilipe David Borba Manana#include <sstream>
26f08dea7dSFilipe David Borba Manana#include <v8.h>
276f4d4854SFilipe David Borba Manana#include <time.h>
28f08dea7dSFilipe David Borba Manana
29f08dea7dSFilipe David Borba Manana#include "mapreduce.h"
30a3512044SNimish Gupta#define MAX_LOG_STRING_SIZE 1024
31f08dea7dSFilipe David Borba Manana
32cb21b8ffSharsha#define MAX_EMIT_KEY_SIZE 4096
33cb21b8ffSharsha
34f08dea7dSFilipe David Borba Mananausing namespace v8;
35f08dea7dSFilipe David Borba Manana
36f08dea7dSFilipe David Borba Mananatypedef struct {
37f08dea7dSFilipe David Borba Manana    Persistent<Object>    jsonObject;
38f08dea7dSFilipe David Borba Manana    Persistent<Function>  jsonParseFun;
39f08dea7dSFilipe David Borba Manana    Persistent<Function>  stringifyFun;
40f08dea7dSFilipe David Borba Manana    map_reduce_ctx_t      *ctx;
41f08dea7dSFilipe David Borba Manana} isolate_data_t;
42f08dea7dSFilipe David Borba Manana
43f08dea7dSFilipe David Borba Manana
44f08dea7dSFilipe David Borba Mananastatic const char *SUM_FUNCTION_STRING =
45f08dea7dSFilipe David Borba Manana    "(function(values) {"
46f08dea7dSFilipe David Borba Manana    "    var sum = 0;"
47f08dea7dSFilipe David Borba Manana    "    for (var i = 0; i < values.length; ++i) {"
48f08dea7dSFilipe David Borba Manana    "        sum += values[i];"
49f08dea7dSFilipe David Borba Manana    "    }"
50f08dea7dSFilipe David Borba Manana    "    return sum;"
51f08dea7dSFilipe David Borba Manana    "})";
52f08dea7dSFilipe David Borba Manana
53e27b8f3cSChris Andersonstatic const char *DATE_FUNCTION_STRING =
54e27b8f3cSChris Anderson    // I wish it was on the prototype, but that will require bigger
55e27b8f3cSChris Anderson    // C changes as adding to the date prototype should be done on
56e27b8f3cSChris Anderson    // process launch. The code you see here may be faster, but it
57e27b8f3cSChris Anderson    // is less JavaScripty.
58e27b8f3cSChris Anderson    // "Date.prototype.toArray = (function() {"
59e27b8f3cSChris Anderson    "(function(date) {"
60e27b8f3cSChris Anderson    "    date = date.getUTCDate ? date : new Date(date);"
61e27b8f3cSChris Anderson    "    return isFinite(date.valueOf()) ?"
62e27b8f3cSChris Anderson    "      [date.getUTCFullYear(),"
63e27b8f3cSChris Anderson    "      (date.getUTCMonth() + 1),"
64e27b8f3cSChris Anderson    "       date.getUTCDate(),"
65e27b8f3cSChris Anderson    "       date.getUTCHours(),"
66e27b8f3cSChris Anderson    "       date.getUTCMinutes(),"
67e27b8f3cSChris Anderson    "       date.getUTCSeconds()] : null;"
68e27b8f3cSChris Anderson    "})";
69e27b8f3cSChris Anderson
70d826b31cSChris Andersonstatic const char *BASE64_FUNCTION_STRING =
71e15fb541SFilipe David Borba Manana    "(function(b64) {"
72e15fb541SFilipe David Borba Manana    "    var i, j, l, tmp, scratch, arr = [];"
73e15fb541SFilipe David Borba Manana    "    var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';"
745b8f80fcSFilipe David Borba Manana    "    if (typeof b64 !== 'string') {"
755b8f80fcSFilipe David Borba Manana    "        throw 'Input is not a string';"
765b8f80fcSFilipe David Borba Manana    "    }"
77e15fb541SFilipe David Borba Manana    "    if (b64.length % 4 > 0) {"
78e15fb541SFilipe David Borba Manana    "        throw 'Invalid base64 source.';"
79e15fb541SFilipe David Borba Manana    "    }"
80e15fb541SFilipe David Borba Manana    "    scratch = b64.indexOf('=');"
81e15fb541SFilipe David Borba Manana    "    scratch = scratch > 0 ? b64.length - scratch : 0;"
82e15fb541SFilipe David Borba Manana    "    l = scratch > 0 ? b64.length - 4 : b64.length;"
83e15fb541SFilipe David Borba Manana    "    for (i = 0, j = 0; i < l; i += 4, j += 3) {"
84e15fb541SFilipe David Borba Manana    "        tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12);"
85e15fb541SFilipe David Borba Manana    "        tmp |= (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]);"
86e15fb541SFilipe David Borba Manana    "        arr.push((tmp & 0xFF0000) >> 16);"
87e15fb541SFilipe David Borba Manana    "        arr.push((tmp & 0xFF00) >> 8);"
88e15fb541SFilipe David Borba Manana    "        arr.push(tmp & 0xFF);"
89e15fb541SFilipe David Borba Manana    "    }"
90e15fb541SFilipe David Borba Manana    "    if (scratch === 2) {"
91e15fb541SFilipe David Borba Manana    "        tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4);"
92e15fb541SFilipe David Borba Manana    "        arr.push(tmp & 0xFF);"
93e15fb541SFilipe David Borba Manana    "    } else if (scratch === 1) {"
94e15fb541SFilipe David Borba Manana    "        tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4);"
95e15fb541SFilipe David Borba Manana    "        tmp |= (lookup.indexOf(b64[i + 2]) >> 2);"
96e15fb541SFilipe David Borba Manana    "        arr.push((tmp >> 8) & 0xFF);"
97e15fb541SFilipe David Borba Manana    "        arr.push(tmp & 0xFF);"
98e15fb541SFilipe David Borba Manana    "    }"
99b935f38eSChris Anderson    "    return arr;"
100e15fb541SFilipe David Borba Manana    "})";
101f08dea7dSFilipe David Borba Manana
102c358a126SVolker Mischestatic void doInitContext(map_reduce_ctx_t *ctx,
103c358a126SVolker Mische                          const function_sources_list_t &funs,
104c358a126SVolker Mische                          const view_index_type_t viewType);
105de272766SSarath Lakshman#ifdef V8_POST_3_19_API
106de272766SSarath Lakshmanstatic Local<Context> createJsContext(map_reduce_ctx_t *ctx);
107de272766SSarath Lakshmanstatic void emit(const v8::FunctionCallbackInfo<Value>& args);
108a3512044SNimish Guptastatic void log(const v8::FunctionCallbackInfo<Value>& args);
109de272766SSarath Lakshman#else
110f08dea7dSFilipe David Borba Mananastatic Persistent<Context> createJsContext(map_reduce_ctx_t *ctx);
111f08dea7dSFilipe David Borba Mananastatic Handle<Value> emit(const Arguments& args);
112a3512044SNimish Guptastatic Handle<Value> log(const Arguments& args);
113de272766SSarath Lakshman#endif
114de272766SSarath Lakshman
115c8003e55SFilipe David Borba Mananastatic void loadFunctions(map_reduce_ctx_t *ctx, const function_sources_list_t &funs);
116cb322508SFilipe David Borba Mananastatic void freeJsonData(const json_results_list_t &data);
117cb322508SFilipe David Borba Mananastatic void freeMapResult(const map_result_t &data);
118cb322508SFilipe David Borba Mananastatic void freeMapResultList(const map_results_list_t &results);
119c8003e55SFilipe David Borba Mananastatic Handle<Function> compileFunction(const function_source_t &funSource);
120079a4bd8SFilipe David Borba Mananastatic inline ErlNifBinary jsonStringify(const Handle<Value> &obj);
121079a4bd8SFilipe David Borba Mananastatic inline Handle<Value> jsonParse(const ErlNifBinary &thing);
122c8003e55SFilipe David Borba Mananastatic inline Handle<Array> jsonListToJsArray(const json_results_list_t &list);
123f08dea7dSFilipe David Borba Mananastatic inline isolate_data_t *getIsolateData();
1246f4d4854SFilipe David Borba Mananastatic inline void taskStarted(map_reduce_ctx_t *ctx);
1256f4d4854SFilipe David Borba Mananastatic inline void taskFinished(map_reduce_ctx_t *ctx);
1262db28bf5SFilipe David Borba Mananastatic std::string exceptionString(const TryCatch &tryCatch);
127a3512044SNimish Guptastatic void freeLogResults(map_reduce_ctx_t *ctx);
128f08dea7dSFilipe David Borba Manana
129f08dea7dSFilipe David Borba Manana
130f08dea7dSFilipe David Borba Manana
131c358a126SVolker Mischevoid initContext(map_reduce_ctx_t *ctx, const function_sources_list_t &funs,
132c358a126SVolker Mische                 const view_index_type_t viewType)
13326986f6cSFilipe David Borba Manana{
134de272766SSarath Lakshman    ctx = new (ctx) map_reduce_ctx_t();
135c358a126SVolker Mische    doInitContext(ctx, funs, viewType);
13626986f6cSFilipe David Borba Manana
13726986f6cSFilipe David Borba Manana    try {
13826986f6cSFilipe David Borba Manana        Locker locker(ctx->isolate);
13926986f6cSFilipe David Borba Manana        Isolate::Scope isolateScope(ctx->isolate);
140de272766SSarath Lakshman#ifdef V8_POST_3_19_API
141de272766SSarath Lakshman        HandleScope handleScope(ctx->isolate);
142de272766SSarath Lakshman        Context::Scope contextScope(ctx->isolate, ctx->jsContext);
143de272766SSarath Lakshman#else
14426986f6cSFilipe David Borba Manana        HandleScope handleScope;
14526986f6cSFilipe David Borba Manana        Context::Scope contextScope(ctx->jsContext);
14626986f6cSFilipe David Borba Manana
147de272766SSarath Lakshman#endif
14826986f6cSFilipe David Borba Manana        loadFunctions(ctx, funs);
14926986f6cSFilipe David Borba Manana    } catch (...) {
15026986f6cSFilipe David Borba Manana        destroyContext(ctx);
15126986f6cSFilipe David Borba Manana        throw;
15226986f6cSFilipe David Borba Manana    }
15326986f6cSFilipe David Borba Manana}
15426986f6cSFilipe David Borba Manana
15526986f6cSFilipe David Borba Manana
156c358a126SVolker Mischevoid doInitContext(map_reduce_ctx_t *ctx, const function_sources_list_t &funs,
157c358a126SVolker Mische                   const view_index_type_t viewType)
158f08dea7dSFilipe David Borba Manana{
159c358a126SVolker Mische    ctx->viewType = viewType;
160f08dea7dSFilipe David Borba Manana    ctx->isolate = Isolate::New();
161f73ecfd2SNimish Gupta    ctx->logResults = NULL;
162f08dea7dSFilipe David Borba Manana    Locker locker(ctx->isolate);
163f08dea7dSFilipe David Borba Manana    Isolate::Scope isolateScope(ctx->isolate);
164de272766SSarath Lakshman#ifdef V8_POST_3_19_API
165de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
166de272766SSarath Lakshman
167de272766SSarath Lakshman    ctx->jsContext.Reset(ctx->isolate, createJsContext(ctx));
168de272766SSarath Lakshman    Local<Context> context = Local<Context>::New(ctx->isolate, ctx->jsContext);
169de272766SSarath Lakshman    Context::Scope contextScope(context);
170de272766SSarath Lakshman
171de272766SSarath Lakshman    Handle<Object> jsonObject = Local<Object>::Cast(context->Global()->Get(String::New("JSON")));
172de272766SSarath Lakshman#else
173f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
174f08dea7dSFilipe David Borba Manana
175f08dea7dSFilipe David Borba Manana    ctx->jsContext = createJsContext(ctx);
176f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(ctx->jsContext);
177f08dea7dSFilipe David Borba Manana
178f08dea7dSFilipe David Borba Manana    Handle<Object> jsonObject = Local<Object>::Cast(ctx->jsContext->Global()->Get(String::New("JSON")));
179de272766SSarath Lakshman#endif
180de272766SSarath Lakshman
181f08dea7dSFilipe David Borba Manana    Handle<Function> parseFun = Local<Function>::Cast(jsonObject->Get(String::New("parse")));
182f08dea7dSFilipe David Borba Manana    Handle<Function> stringifyFun = Local<Function>::Cast(jsonObject->Get(String::New("stringify")));
183f08dea7dSFilipe David Borba Manana
184c8003e55SFilipe David Borba Manana    isolate_data_t *isoData = (isolate_data_t *) enif_alloc(sizeof(isolate_data_t));
185c8003e55SFilipe David Borba Manana    if (isoData == NULL) {
186c8003e55SFilipe David Borba Manana        throw std::bad_alloc();
187c8003e55SFilipe David Borba Manana    }
188de272766SSarath Lakshman
189de272766SSarath Lakshman    isoData = new (isoData) isolate_data_t();
190de272766SSarath Lakshman#ifdef V8_POST_3_19_API
191de272766SSarath Lakshman    isoData->jsonObject.Reset(ctx->isolate, jsonObject);
192de272766SSarath Lakshman    isoData->jsonParseFun.Reset(ctx->isolate, parseFun);
193de272766SSarath Lakshman    isoData->stringifyFun.Reset(ctx->isolate, stringifyFun);
194de272766SSarath Lakshman#else
195f08dea7dSFilipe David Borba Manana    isoData->jsonObject = Persistent<Object>::New(jsonObject);
196f08dea7dSFilipe David Borba Manana    isoData->jsonParseFun = Persistent<Function>::New(parseFun);
197f08dea7dSFilipe David Borba Manana    isoData->stringifyFun = Persistent<Function>::New(stringifyFun);
198de272766SSarath Lakshman#endif
199de272766SSarath Lakshman
200f08dea7dSFilipe David Borba Manana    isoData->ctx = ctx;
201f08dea7dSFilipe David Borba Manana
202f08dea7dSFilipe David Borba Manana    ctx->isolate->SetData(isoData);
203ca9e1ad1Sharsha    ctx->taskStartTime = 0;
204f08dea7dSFilipe David Borba Manana}
205f08dea7dSFilipe David Borba Manana
206f08dea7dSFilipe David Borba Manana
207cb322508SFilipe David Borba Mananamap_results_list_t mapDoc(map_reduce_ctx_t *ctx,
208cb322508SFilipe David Borba Manana                          const ErlNifBinary &doc,
209cb322508SFilipe David Borba Manana                          const ErlNifBinary &meta)
210f08dea7dSFilipe David Borba Manana{
211f08dea7dSFilipe David Borba Manana    Locker locker(ctx->isolate);
212f08dea7dSFilipe David Borba Manana    Isolate::Scope isolateScope(ctx->isolate);
213de272766SSarath Lakshman#ifdef V8_POST_3_19_API
214de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
215de272766SSarath Lakshman    Context::Scope contextScope(ctx->isolate, ctx->jsContext);
216de272766SSarath Lakshman#else
217f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
218f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(ctx->jsContext);
219de272766SSarath Lakshman#endif
22023001b64SVolker Mische    Handle<Value> docObject = jsonParse(doc);
221b935f38eSChris Anderson    Handle<Value> metaObject = jsonParse(meta);
222f08dea7dSFilipe David Borba Manana
223b935f38eSChris Anderson    if (!metaObject->IsObject()) {
224b935f38eSChris Anderson        throw MapReduceError("metadata is not a JSON object");
225f08dea7dSFilipe David Borba Manana    }
226f08dea7dSFilipe David Borba Manana
227cb322508SFilipe David Borba Manana    map_results_list_t results;
228b935f38eSChris Anderson    Handle<Value> funArgs[] = { docObject, metaObject };
229f08dea7dSFilipe David Borba Manana
2306f4d4854SFilipe David Borba Manana    taskStarted(ctx);
2316f4d4854SFilipe David Borba Manana
232314270cbSFilipe David Borba Manana    for (unsigned int i = 0; i < ctx->functions->size(); ++i) {
233cb322508SFilipe David Borba Manana        map_result_t mapResult;
234de272766SSarath Lakshman#ifdef V8_POST_3_19_API
235de272766SSarath Lakshman        Local<Function> fun = Local<Function>::New(ctx->isolate, *(*ctx->functions)[i]);
236de272766SSarath Lakshman#else
237f08dea7dSFilipe David Borba Manana        Handle<Function> fun = (*ctx->functions)[i];
238de272766SSarath Lakshman#endif
239f08dea7dSFilipe David Borba Manana        TryCatch trycatch;
240f08dea7dSFilipe David Borba Manana
241cb322508SFilipe David Borba Manana        mapResult.type = MAP_KVS;
242cb322508SFilipe David Borba Manana        mapResult.result.kvs = (kv_pair_list_t *) enif_alloc(sizeof(kv_pair_list_t));
243cb322508SFilipe David Borba Manana
244cb322508SFilipe David Borba Manana        if (mapResult.result.kvs == NULL) {
245cb322508SFilipe David Borba Manana            freeMapResultList(results);
246cb322508SFilipe David Borba Manana            throw std::bad_alloc();
247cb322508SFilipe David Borba Manana        }
248cb322508SFilipe David Borba Manana
249cb322508SFilipe David Borba Manana        mapResult.result.kvs = new (mapResult.result.kvs) kv_pair_list_t();
250cb322508SFilipe David Borba Manana        ctx->kvs = mapResult.result.kvs;
2515297f10dSFilipe David Borba Manana        ctx->emitKvSize = 0;
252b935f38eSChris Anderson        Handle<Value> result = fun->Call(fun, 2, funArgs);
253f08dea7dSFilipe David Borba Manana
254f08dea7dSFilipe David Borba Manana        if (result.IsEmpty()) {
255cb322508SFilipe David Borba Manana            freeMapResult(mapResult);
256f08dea7dSFilipe David Borba Manana
2576f4d4854SFilipe David Borba Manana            if (!trycatch.CanContinue()) {
258cb322508SFilipe David Borba Manana                freeMapResultList(results);
2596f4d4854SFilipe David Borba Manana                throw MapReduceError("timeout");
2606f4d4854SFilipe David Borba Manana            }
2616f4d4854SFilipe David Borba Manana
262cb322508SFilipe David Borba Manana            mapResult.type = MAP_ERROR;
263cb322508SFilipe David Borba Manana            std::string exceptString = exceptionString(trycatch);
264cb322508SFilipe David Borba Manana            size_t len = exceptString.length();
265cb322508SFilipe David Borba Manana
266cb322508SFilipe David Borba Manana            mapResult.result.error = (ErlNifBinary *) enif_alloc(sizeof(ErlNifBinary));
267cb322508SFilipe David Borba Manana            if (mapResult.result.error == NULL) {
268cb322508SFilipe David Borba Manana                freeMapResultList(results);
269cb322508SFilipe David Borba Manana                throw std::bad_alloc();
270cb322508SFilipe David Borba Manana            }
271cb322508SFilipe David Borba Manana            if (!enif_alloc_binary_compat(ctx->env, len, mapResult.result.error)) {
272cb322508SFilipe David Borba Manana                freeMapResultList(results);
273cb322508SFilipe David Borba Manana                throw std::bad_alloc();
274cb322508SFilipe David Borba Manana            }
275cb322508SFilipe David Borba Manana            // Caller responsible for invoking enif_make_binary() or enif_release_binary()
276cb322508SFilipe David Borba Manana            memcpy(mapResult.result.error->data, exceptString.data(), len);
277f08dea7dSFilipe David Borba Manana        }
278f08dea7dSFilipe David Borba Manana
279cb322508SFilipe David Borba Manana        results.push_back(mapResult);
280f08dea7dSFilipe David Borba Manana    }
281f08dea7dSFilipe David Borba Manana
2826f4d4854SFilipe David Borba Manana    taskFinished(ctx);
2836f4d4854SFilipe David Borba Manana
284f08dea7dSFilipe David Borba Manana    return results;
285f08dea7dSFilipe David Borba Manana}
286f08dea7dSFilipe David Borba Manana
287f08dea7dSFilipe David Borba Manana
288c8003e55SFilipe David Borba Mananajson_results_list_t runReduce(map_reduce_ctx_t *ctx,
289c8003e55SFilipe David Borba Manana                              const json_results_list_t &keys,
290c8003e55SFilipe David Borba Manana                              const json_results_list_t &values)
291f08dea7dSFilipe David Borba Manana{
292f08dea7dSFilipe David Borba Manana    Locker locker(ctx->isolate);
293f08dea7dSFilipe David Borba Manana    Isolate::Scope isolateScope(ctx->isolate);
294de272766SSarath Lakshman#ifdef V8_POST_3_19_API
295de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
296de272766SSarath Lakshman    Context::Scope contextScope(ctx->isolate, ctx->jsContext);
297de272766SSarath Lakshman#else
298f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
299f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(ctx->jsContext);
300de272766SSarath Lakshman#endif
301f08dea7dSFilipe David Borba Manana    Handle<Array> keysArray = jsonListToJsArray(keys);
302f08dea7dSFilipe David Borba Manana    Handle<Array> valuesArray = jsonListToJsArray(values);
303c8003e55SFilipe David Borba Manana    json_results_list_t results;
304f08dea7dSFilipe David Borba Manana
305f08dea7dSFilipe David Borba Manana    Handle<Value> args[] = { keysArray, valuesArray, Boolean::New(false) };
306f08dea7dSFilipe David Borba Manana
3076f4d4854SFilipe David Borba Manana    taskStarted(ctx);
3086f4d4854SFilipe David Borba Manana
309314270cbSFilipe David Borba Manana    for (unsigned int i = 0; i < ctx->functions->size(); ++i) {
310de272766SSarath Lakshman#ifdef V8_POST_3_19_API
311de272766SSarath Lakshman        Local<Function> fun = Local<Function>::New(ctx->isolate, *(*ctx->functions)[i]);
312de272766SSarath Lakshman#else
313f08dea7dSFilipe David Borba Manana        Handle<Function> fun = (*ctx->functions)[i];
314de272766SSarath Lakshman#endif
315f08dea7dSFilipe David Borba Manana        TryCatch trycatch;
316f08dea7dSFilipe David Borba Manana        Handle<Value> result = fun->Call(fun, 3, args);
317f08dea7dSFilipe David Borba Manana
318f08dea7dSFilipe David Borba Manana        if (result.IsEmpty()) {
319cb322508SFilipe David Borba Manana            freeJsonData(results);
320f08dea7dSFilipe David Borba Manana
3216f4d4854SFilipe David Borba Manana            if (!trycatch.CanContinue()) {
3226f4d4854SFilipe David Borba Manana                throw MapReduceError("timeout");
3236f4d4854SFilipe David Borba Manana            }
3246f4d4854SFilipe David Borba Manana
3252db28bf5SFilipe David Borba Manana            throw MapReduceError(exceptionString(trycatch));
326f08dea7dSFilipe David Borba Manana        }
327f08dea7dSFilipe David Borba Manana
328b3cced50SFilipe David Borba Manana        try {
329079a4bd8SFilipe David Borba Manana            ErlNifBinary jsonResult = jsonStringify(result);
330b3cced50SFilipe David Borba Manana            results.push_back(jsonResult);
331b3cced50SFilipe David Borba Manana        } catch(...) {
332cb322508SFilipe David Borba Manana            freeJsonData(results);
333b3cced50SFilipe David Borba Manana            throw;
334b3cced50SFilipe David Borba Manana        }
335f08dea7dSFilipe David Borba Manana    }
336f08dea7dSFilipe David Borba Manana
3376f4d4854SFilipe David Borba Manana    taskFinished(ctx);
338a3512044SNimish Gupta    freeLogResults(ctx);
3396f4d4854SFilipe David Borba Manana
340f08dea7dSFilipe David Borba Manana    return results;
341f08dea7dSFilipe David Borba Manana}
342f08dea7dSFilipe David Borba Manana
343f08dea7dSFilipe David Borba Manana
344079a4bd8SFilipe David Borba MananaErlNifBinary runReduce(map_reduce_ctx_t *ctx,
345079a4bd8SFilipe David Borba Manana                       int reduceFunNum,
346079a4bd8SFilipe David Borba Manana                       const json_results_list_t &keys,
347079a4bd8SFilipe David Borba Manana                       const json_results_list_t &values)
348f08dea7dSFilipe David Borba Manana{
349f08dea7dSFilipe David Borba Manana    Locker locker(ctx->isolate);
350f08dea7dSFilipe David Borba Manana    Isolate::Scope isolateScope(ctx->isolate);
351de272766SSarath Lakshman#ifdef V8_POST_3_19_API
352de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
353de272766SSarath Lakshman    Context::Scope contextScope(ctx->isolate, ctx->jsContext);
354de272766SSarath Lakshman#else
355f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
356f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(ctx->jsContext);
357de272766SSarath Lakshman#endif
358f08dea7dSFilipe David Borba Manana
359f08dea7dSFilipe David Borba Manana    reduceFunNum -= 1;
360314270cbSFilipe David Borba Manana    if (reduceFunNum < 0 ||
361314270cbSFilipe David Borba Manana        static_cast<unsigned int>(reduceFunNum) >= ctx->functions->size()) {
362f08dea7dSFilipe David Borba Manana        throw MapReduceError("invalid reduce function number");
363f08dea7dSFilipe David Borba Manana    }
364f08dea7dSFilipe David Borba Manana
365de272766SSarath Lakshman#ifdef V8_POST_3_19_API
366de272766SSarath Lakshman    Local<Function> fun = Local<Function>::New(ctx->isolate, *(*ctx->functions)[reduceFunNum]);
367de272766SSarath Lakshman#else
368f08dea7dSFilipe David Borba Manana    Handle<Function> fun = (*ctx->functions)[reduceFunNum];
369de272766SSarath Lakshman#endif
370f08dea7dSFilipe David Borba Manana    Handle<Array> keysArray = jsonListToJsArray(keys);
371f08dea7dSFilipe David Borba Manana    Handle<Array> valuesArray = jsonListToJsArray(values);
372f08dea7dSFilipe David Borba Manana    Handle<Value> args[] = { keysArray, valuesArray, Boolean::New(false) };
373f08dea7dSFilipe David Borba Manana
3746f4d4854SFilipe David Borba Manana    taskStarted(ctx);
3756f4d4854SFilipe David Borba Manana
376f08dea7dSFilipe David Borba Manana    TryCatch trycatch;
377f08dea7dSFilipe David Borba Manana    Handle<Value> result = fun->Call(fun, 3, args);
378f08dea7dSFilipe David Borba Manana
3796f4d4854SFilipe David Borba Manana    taskFinished(ctx);
380a3512044SNimish Gupta    freeLogResults(ctx);
3816f4d4854SFilipe David Borba Manana
382f08dea7dSFilipe David Borba Manana    if (result.IsEmpty()) {
3836f4d4854SFilipe David Borba Manana        if (!trycatch.CanContinue()) {
3846f4d4854SFilipe David Borba Manana            throw MapReduceError("timeout");
3856f4d4854SFilipe David Borba Manana        }
3866f4d4854SFilipe David Borba Manana
3872db28bf5SFilipe David Borba Manana        throw MapReduceError(exceptionString(trycatch));
388f08dea7dSFilipe David Borba Manana    }
389f08dea7dSFilipe David Borba Manana
390f08dea7dSFilipe David Borba Manana    return jsonStringify(result);
391f08dea7dSFilipe David Borba Manana}
392f08dea7dSFilipe David Borba Manana
393f08dea7dSFilipe David Borba Manana
394079a4bd8SFilipe David Borba MananaErlNifBinary runRereduce(map_reduce_ctx_t *ctx,
395c8003e55SFilipe David Borba Manana                       int reduceFunNum,
396c8003e55SFilipe David Borba Manana                       const json_results_list_t &reductions)
397f08dea7dSFilipe David Borba Manana{
398f08dea7dSFilipe David Borba Manana    Locker locker(ctx->isolate);
399f08dea7dSFilipe David Borba Manana    Isolate::Scope isolateScope(ctx->isolate);
400de272766SSarath Lakshman#ifdef V8_POST_3_19_API
401de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
402de272766SSarath Lakshman    Context::Scope contextScope(ctx->isolate, ctx->jsContext);
403de272766SSarath Lakshman#else
404f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
405f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(ctx->jsContext);
406de272766SSarath Lakshman#endif
407f08dea7dSFilipe David Borba Manana
408f08dea7dSFilipe David Borba Manana    reduceFunNum -= 1;
409314270cbSFilipe David Borba Manana    if (reduceFunNum < 0 ||
410314270cbSFilipe David Borba Manana        static_cast<unsigned int>(reduceFunNum) >= ctx->functions->size()) {
411f08dea7dSFilipe David Borba Manana        throw MapReduceError("invalid reduce function number");
412f08dea7dSFilipe David Borba Manana    }
413f08dea7dSFilipe David Borba Manana
414de272766SSarath Lakshman#ifdef V8_POST_3_19_API
415de272766SSarath Lakshman    Local<Function> fun = Local<Function>::New(ctx->isolate, *(*ctx->functions)[reduceFunNum]);
416de272766SSarath Lakshman#else
417f08dea7dSFilipe David Borba Manana    Handle<Function> fun = (*ctx->functions)[reduceFunNum];
418de272766SSarath Lakshman#endif
419f08dea7dSFilipe David Borba Manana    Handle<Array> valuesArray = jsonListToJsArray(reductions);
420f08dea7dSFilipe David Borba Manana    Handle<Value> args[] = { Null(), valuesArray, Boolean::New(true) };
421f08dea7dSFilipe David Borba Manana
4226f4d4854SFilipe David Borba Manana    taskStarted(ctx);
4236f4d4854SFilipe David Borba Manana
424f08dea7dSFilipe David Borba Manana    TryCatch trycatch;
425f08dea7dSFilipe David Borba Manana    Handle<Value> result = fun->Call(fun, 3, args);
426f08dea7dSFilipe David Borba Manana
4276f4d4854SFilipe David Borba Manana    taskFinished(ctx);
428a3512044SNimish Gupta    freeLogResults(ctx);
4296f4d4854SFilipe David Borba Manana
430f08dea7dSFilipe David Borba Manana    if (result.IsEmpty()) {
4316f4d4854SFilipe David Borba Manana        if (!trycatch.CanContinue()) {
4326f4d4854SFilipe David Borba Manana            throw MapReduceError("timeout");
4336f4d4854SFilipe David Borba Manana        }
4346f4d4854SFilipe David Borba Manana
4352db28bf5SFilipe David Borba Manana        throw MapReduceError(exceptionString(trycatch));
436f08dea7dSFilipe David Borba Manana    }
437f08dea7dSFilipe David Borba Manana
438f08dea7dSFilipe David Borba Manana    return jsonStringify(result);
439f08dea7dSFilipe David Borba Manana}
440f08dea7dSFilipe David Borba Manana
441f08dea7dSFilipe David Borba Manana
442f08dea7dSFilipe David Borba Mananavoid destroyContext(map_reduce_ctx_t *ctx)
443f08dea7dSFilipe David Borba Manana{
444f08dea7dSFilipe David Borba Manana    {
445f08dea7dSFilipe David Borba Manana        Locker locker(ctx->isolate);
446f08dea7dSFilipe David Borba Manana        Isolate::Scope isolateScope(ctx->isolate);
447de272766SSarath Lakshman#ifdef V8_POST_3_19_API
448de272766SSarath Lakshman        HandleScope handleScope(ctx->isolate);
449de272766SSarath Lakshman        Context::Scope contextScope(ctx->isolate, ctx->jsContext);
450de272766SSarath Lakshman#else
451f08dea7dSFilipe David Borba Manana        HandleScope handleScope;
452f08dea7dSFilipe David Borba Manana        Context::Scope contextScope(ctx->jsContext);
453de272766SSarath Lakshman#endif
454f08dea7dSFilipe David Borba Manana
455314270cbSFilipe David Borba Manana        for (unsigned int i = 0; i < ctx->functions->size(); ++i) {
456de272766SSarath Lakshman#ifdef V8_POST_3_19_API
457de272766SSarath Lakshman            (*ctx->functions)[i]->Dispose();
458de272766SSarath Lakshman            (*ctx->functions)[i]->~Persistent<v8::Function>();
459de272766SSarath Lakshman            enif_free((*ctx->functions)[i]);
460de272766SSarath Lakshman#else
461f08dea7dSFilipe David Borba Manana            (*ctx->functions)[i].Dispose();
462de272766SSarath Lakshman#endif
463f08dea7dSFilipe David Borba Manana        }
464c8003e55SFilipe David Borba Manana        ctx->functions->~function_vector_t();
465c8003e55SFilipe David Borba Manana        enif_free(ctx->functions);
466f08dea7dSFilipe David Borba Manana
467f08dea7dSFilipe David Borba Manana        isolate_data_t *isoData = getIsolateData();
468f08dea7dSFilipe David Borba Manana        isoData->jsonObject.Dispose();
469f08dea7dSFilipe David Borba Manana        isoData->jsonObject.Clear();
470f08dea7dSFilipe David Borba Manana        isoData->jsonParseFun.Dispose();
471f08dea7dSFilipe David Borba Manana        isoData->jsonParseFun.Clear();
472f08dea7dSFilipe David Borba Manana        isoData->stringifyFun.Dispose();
473f08dea7dSFilipe David Borba Manana        isoData->stringifyFun.Clear();
474de272766SSarath Lakshman        isoData->~isolate_data_t();
475c8003e55SFilipe David Borba Manana        enif_free(isoData);
476f08dea7dSFilipe David Borba Manana
477f08dea7dSFilipe David Borba Manana        ctx->jsContext.Dispose();
478f08dea7dSFilipe David Borba Manana        ctx->jsContext.Clear();
479f08dea7dSFilipe David Borba Manana    }
480f08dea7dSFilipe David Borba Manana
481f08dea7dSFilipe David Borba Manana    ctx->isolate->Dispose();
482de272766SSarath Lakshman    ctx->~map_reduce_ctx_t();
483f08dea7dSFilipe David Borba Manana}
484f08dea7dSFilipe David Borba Manana
485f08dea7dSFilipe David Borba Manana
486de272766SSarath Lakshman#ifdef V8_POST_3_19_API
487de272766SSarath Lakshmanstatic Local<Context> createJsContext(map_reduce_ctx_t *ctx)
488de272766SSarath Lakshman{
489de272766SSarath Lakshman    HandleScope handleScope(ctx->isolate);
490de272766SSarath Lakshman#else
491f08dea7dSFilipe David Borba MananaPersistent<Context> createJsContext(map_reduce_ctx_t *ctx)
492f08dea7dSFilipe David Borba Manana{
493f08dea7dSFilipe David Borba Manana    HandleScope handleScope;
494de272766SSarath Lakshman#endif
495f08dea7dSFilipe David Borba Manana    Handle<ObjectTemplate> global = ObjectTemplate::New();
496f08dea7dSFilipe David Borba Manana
497f08dea7dSFilipe David Borba Manana    global->Set(String::New("emit"), FunctionTemplate::New(emit));
498f08dea7dSFilipe David Borba Manana
499a3512044SNimish Gupta    global->Set(String::New("log"), FunctionTemplate::New(log));
500a3512044SNimish Gupta
501de272766SSarath Lakshman#ifdef V8_POST_3_19_API
502de272766SSarath Lakshman    Handle<Context> context = Context::New(ctx->isolate, NULL, global);
503de272766SSarath Lakshman#else
504f08dea7dSFilipe David Borba Manana    Persistent<Context> context = Context::New(NULL, global);
505de272766SSarath Lakshman#endif
506f08dea7dSFilipe David Borba Manana    Context::Scope contextScope(context);
507f08dea7dSFilipe David Borba Manana
508f08dea7dSFilipe David Borba Manana    Handle<Function> sumFun = compileFunction(SUM_FUNCTION_STRING);
509f08dea7dSFilipe David Borba Manana    context->Global()->Set(String::New("sum"), sumFun);
510f08dea7dSFilipe David Borba Manana
511d826b31cSChris Anderson    Handle<Function> decodeBase64Fun = compileFunction(BASE64_FUNCTION_STRING);
512d826b31cSChris Anderson    context->Global()->Set(String::New("decodeBase64"), decodeBase64Fun);
513d826b31cSChris Anderson
514e27b8f3cSChris Anderson    Handle<Function> dateToArrayFun = compileFunction(DATE_FUNCTION_STRING);
515e27b8f3cSChris Anderson    context->Global()->Set(String::New("dateToArray"), dateToArrayFun);
516e27b8f3cSChris Anderson
517