//////////////////////////////////////////////////////////////////////////////// /// @brief V8 utility functions /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// 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. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 #include "BasicsC/win-utils.h" #endif #include "v8-utils.h" #include "BasicsC/common.h" #include #include #include #include "Basics/Dictionary.h" #include "Basics/Nonce.h" #include "Basics/RandomGenerator.h" #include "Basics/StringUtils.h" #include "BasicsC/conversions.h" #include "BasicsC/csv.h" #include "BasicsC/files.h" #include "BasicsC/logging.h" #include "BasicsC/process-utils.h" #include "BasicsC/string-buffer.h" #include "BasicsC/tri-strings.h" #include "BasicsC/tri-zip.h" #include "BasicsC/utf8-helper.h" #include "Basics/FileUtils.h" #include "Rest/HttpRequest.h" #include "Rest/SslInterface.h" #include "SimpleHttpClient/GeneralClientConnection.h" #include "SimpleHttpClient/SimpleHttpClient.h" #include "SimpleHttpClient/SimpleHttpResult.h" #include "Statistics/statistics.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" #include "unicode/normalizer2.h" #include "3rdParty/valgrind/valgrind.h" using namespace std; using namespace triagens::arango; using namespace triagens::basics; using namespace triagens::httpclient; using namespace triagens::rest; // ----------------------------------------------------------------------------- // --SECTION-- GENERAL // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Random string generators //////////////////////////////////////////////////////////////////////////////// namespace { static Random::UniformCharacter JSAlphaNumGenerator("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); static Random::UniformCharacter JSNumGenerator("0123456789"); static Random::UniformCharacter JSSaltGenerator("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(){}[]:;<>,.?/|"); } // ----------------------------------------------------------------------------- // --SECTION-- public classes // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Converts an object to a UTF-8-encoded and normalized character array. //////////////////////////////////////////////////////////////////////////////// TRI_Utf8ValueNFC::TRI_Utf8ValueNFC (TRI_memory_zone_t* memoryZone, v8::Handle obj) : _str(0), _length(0), _memoryZone(memoryZone) { v8::String::Value str(obj); int str_len = str.length(); _str = TRI_normalize_utf16_to_NFC(_memoryZone, *str, (size_t) str_len, &_length); } TRI_Utf8ValueNFC::~TRI_Utf8ValueNFC () { if (_str) { TRI_Free(_memoryZone, _str); } } // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief create a Javascript error object //////////////////////////////////////////////////////////////////////////////// static v8::Handle CreateErrorObject (int errorNumber, string const& message) { v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); v8::Handle errorMessage = v8::String::New(message.c_str(), (int) message.size()); v8::Handle errorObject = v8::Exception::Error(errorMessage)->ToObject(); errorObject->Set(v8::String::New("errorNum"), v8::Number::New(errorNumber)); errorObject->Set(v8::String::New("errorMessage"), errorMessage); v8::Handle proto = v8g->ArangoErrorTempl->NewInstance(); if (! proto.IsEmpty()) { errorObject->SetPrototype(proto); } return scope.Close(errorObject); } //////////////////////////////////////////////////////////////////////////////// /// @brief reads/execute a file into/in the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptFile (char const* filename, bool execute, bool useGlobalContext) { v8::HandleScope handleScope; char* content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, filename, NULL); if (content == 0) { LOG_TRACE("cannot load java script file '%s': %s", filename, TRI_last_error()); return false; } if (useGlobalContext) { char* contentWrapper = TRI_Concatenate3StringZ(TRI_UNKNOWN_MEM_ZONE, "(function() { ", content, "/* end-of-file */ })()"); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); content = contentWrapper; } v8::Handle name = v8::String::New(filename); v8::Handle source = v8::String::New(content); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); v8::Handle script = v8::Script::Compile(source, name); // compilation failed, print errors that happened during compilation if (script.IsEmpty()) { return false; } if (execute) { // execute script v8::Handle result = script->Run(); if (result.IsEmpty()) { return false; } } LOG_TRACE("loaded java script file: '%s'", filename); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief reads all files from a directory into the current context //////////////////////////////////////////////////////////////////////////////// static bool LoadJavaScriptDirectory (char const* path, bool execute, bool useGlobalContext) { v8::HandleScope scope; TRI_vector_string_t files; bool result; regex_t re; size_t i; LOG_TRACE("loading JavaScript directory: '%s'", path); files = TRI_FilesDirectory(path); regcomp(&re, "^(.*)\\.js$", REG_ICASE | REG_EXTENDED); result = true; for (i = 0; i < files._length; ++i) { v8::TryCatch tryCatch; bool ok; char const* filename; char* full; filename = files._buffer[i]; if (regexec(&re, filename, 0, 0, 0) != 0) { continue; } full = TRI_Concatenate2File(path, filename); ok = LoadJavaScriptFile(full, execute, useGlobalContext); TRI_FreeString(TRI_CORE_MEM_ZONE, full); result = result && ok; if (! ok) { TRI_LogV8Exception(&tryCatch); } } TRI_DestroyVectorString(&files); regfree(&re); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a distribution vector //////////////////////////////////////////////////////////////////////////////// static v8::Handle DistributionList (StatisticsVector const& dist) { v8::HandleScope scope; v8::Handle result = v8::Array::New(); for (uint32_t i = 0; i < (uint32_t) dist._value.size(); ++i) { result->Set(i, v8::Number::New(dist._value[i])); } return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief fills the distribution //////////////////////////////////////////////////////////////////////////////// static void FillDistribution (v8::Handle list, char const* name, StatisticsDistribution const& dist) { v8::Handle result = v8::Object::New(); result->Set(TRI_V8_SYMBOL("sum"), v8::Number::New(dist._total)); result->Set(TRI_V8_SYMBOL("count"), v8::Number::New(dist._count)); v8::Handle counts = v8::Array::New((int) dist._counts.size()); uint32_t pos = 0; for (vector::const_iterator i = dist._counts.begin(); i != dist._counts.end(); ++i, ++pos) { counts->Set(pos, v8::Number::New(*i)); } result->Set(TRI_V8_SYMBOL("counts"), counts); list->Set(TRI_V8_SYMBOL(name), result); } // ----------------------------------------------------------------------------- // --SECTION-- JS functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief decodes a base64-encoded string /// /// @FUN{internal.base64Decode(@FA{value})} /// /// Base64-decodes the string @FA{value}. //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Base64Decode (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "base64Decode()"); } try { string const value = TRI_ObjectToString(argv[0]); string const base64 = StringUtils::decodeBase64(value); return scope.Close(v8::String::New(base64.c_str(), (int) base64.size())); } catch (...) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), TRI_last_error()); } assert(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief encodes a string as base64 /// /// @FUN{internal.base64Encode(@FA{value})} /// /// Base64-encodes the string @FA{value}. //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Base64Encode (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "base64Encode()"); } try { string const value = TRI_ObjectToString(argv[0]); string const base64 = StringUtils::encodeBase64(value); return scope.Close(v8::String::New(base64.c_str(), (int) base64.size())); } catch (...) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), TRI_last_error()); } assert(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief parses a Javascript snippet, but do not execute it /// /// @FUN{internal.parse(@FA{script})} /// /// Parses the @FA{script} code, but does not execute it. /// Will return @LIT{true} if the code does not have a parse error, and throw /// an exception otherwise. //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Parse (v8::Arguments const& argv) { v8::HandleScope scope; v8::TryCatch tryCatch; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "parse(