//////////////////////////////////////////////////////////////////////////////// /// @brief V8 shell functions /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2011 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-2010, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "v8-shell.h" #define TRI_WITHIN_C #include #include #include #include #undef TRI_WITHIN_C #include #include "V8/v8-json.h" using namespace std; // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Shell V8 Shell /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief begins a new CSV line //////////////////////////////////////////////////////////////////////////////// static void ProcessCsvBegin (TRI_csv_parser_t* parser, size_t row) { v8::Handle* array = reinterpret_cast*>(parser->_dataBegin); (*array) = v8::Array::New(); } //////////////////////////////////////////////////////////////////////////////// /// @brief adds a new CSV field //////////////////////////////////////////////////////////////////////////////// static void ProcessCsvAdd (TRI_csv_parser_t* parser, char const* field, size_t row, size_t column) { v8::Handle* array = reinterpret_cast*>(parser->_dataBegin); (*array)->Set(column, v8::String::New(field)); } //////////////////////////////////////////////////////////////////////////////// /// @brief ends a CSV line //////////////////////////////////////////////////////////////////////////////// static void ProcessCsvEnd (TRI_csv_parser_t* parser, char const* field, size_t row, size_t column) { v8::Handle* array = reinterpret_cast*>(parser->_dataBegin); (*array)->Set(column, v8::String::New(field)); v8::Handle* cb = reinterpret_cast*>(parser->_dataEnd); v8::Handle r = v8::Number::New(row); v8::Handle args[] = { *array, r }; (*cb)->Call(*cb, 2, args); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- JS functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Shell V8 Shell /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief changes the log-level //////////////////////////////////////////////////////////////////////////////// static v8::Handle LogLevelCall (v8::Arguments const& argv) { v8::HandleScope scope; if (1 <= argv.Length()) { v8::String::Utf8Value str(argv[0]); TRI_SetLogLevelLogging(*str); } return scope.Close(v8::String::New(TRI_GetLogLevelLogging())); } //////////////////////////////////////////////////////////////////////////////// /// @brief converts JSON string to object //////////////////////////////////////////////////////////////////////////////// static v8::Handle FromJsonCall (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { return scope.Close(v8::ThrowException(v8::String::New("usage: fromJson()"))); } v8::String::Utf8Value str(argv[0]); if (*str == 0) { return scope.Close(v8::ThrowException(v8::String::New("cannot get UTF-8 representation of "))); } char* error; v8::Handle object = TRI_FromJsonString(*str, &error); if (object->IsUndefined()) { v8::Handle msg = v8::String::New(error); TRI_FreeString(error); return scope.Close(v8::ThrowException(msg)); } return scope.Close(object); } //////////////////////////////////////////////////////////////////////////////// /// @brief outputs the arguments //////////////////////////////////////////////////////////////////////////////// static v8::Handle OutputCall (v8::Arguments const& argv) { for (int i = 0; i < argv.Length(); i++) { v8::HandleScope scope; // extract the next argument v8::Handle val = argv[i]; // convert it into a string v8::String::Utf8Value utf8(val); if (*utf8 == 0) { continue; } // print the argument char const* ptr = *utf8; size_t len = utf8.length(); while (0 < len) { ssize_t n = write(1, ptr, len); if (n < 0) { return v8::Undefined(); } len -= n; ptr += n; } } return v8::Undefined(); } //////////////////////////////////////////////////////////////////////////////// /// @brief prints the arguments //////////////////////////////////////////////////////////////////////////////// static v8::Handle PrintCall (v8::Arguments const& argv) { v8::Handle toJson = v8::Handle::Cast( argv.Holder()->CreationContext()->Global()->Get(v8::String::New("toJson"))); for (int i = 0; i < argv.Length(); i++) { v8::HandleScope scope; TRI_string_buffer_t buffer; TRI_InitStringBuffer(&buffer); // extract the next argument v8::Handle val = argv[i]; // convert the object into json - if possible if (val->IsObject()) { v8::Handle obj = val->ToObject(); v8::Handle printFuncName = v8::String::New("print"); if (obj->Has(printFuncName)) { v8::Handle print = v8::Handle::Cast(obj->Get(printFuncName)); v8::Handle args[] = { val }; print->Call(obj, 1, args); continue; } v8::Handle args[] = { val }; v8::Handle cnv = toJson->Call(toJson, 1, args); v8::String::Utf8Value utf8(cnv); if (*utf8 == 0) { TRI_AppendStringStringBuffer(&buffer, "[object]"); } else { TRI_AppendString2StringBuffer(&buffer, *utf8, utf8.length()); } } else if (val->IsString()) { v8::String::Utf8Value utf8(val->ToString()); if (*utf8 == 0) { TRI_AppendStringStringBuffer(&buffer, "[string]"); } else { TRI_AppendCharStringBuffer(&buffer, '"'); TRI_AppendString2StringBuffer(&buffer, *utf8, utf8.length()); TRI_AppendCharStringBuffer(&buffer, '"'); } } else { v8::String::Utf8Value utf8(val); if (*utf8 == 0) { TRI_AppendStringStringBuffer(&buffer, "[value]"); } else { TRI_AppendString2StringBuffer(&buffer, *utf8, utf8.length()); } } // print the argument char const* ptr = TRI_BeginStringBuffer(&buffer); size_t len = TRI_LengthStringBuffer(&buffer); while (0 < len) { ssize_t n = write(1, ptr, len); if (n < 0) { TRI_DestroyStringBuffer(&buffer); return v8::Undefined(); } len -= n; ptr += n; } // print a new line ssize_t n = write(1, "\n", 1); if (n < 0) { TRI_DestroyStringBuffer(&buffer); return v8::Undefined(); } TRI_DestroyStringBuffer(&buffer); } return v8::Undefined(); } //////////////////////////////////////////////////////////////////////////////// /// @brief processes a csv file //////////////////////////////////////////////////////////////////////////////// static v8::Handle ProcessCsvFileCall (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 2) { return scope.Close(v8::ThrowException(v8::String::New("usage: processCsvFile(, [, ])"))); } // extract the filename v8::String::Utf8Value filename(argv[0]); if (*filename == 0) { return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); } // extract the callback v8::Handle cb = v8::Handle::Cast(argv[1]); // extract the options v8::Handle separatorKey = v8::String::New("separator"); v8::Handle quoteKey = v8::String::New("quote"); char separator = ','; char quote = '"'; if (3 <= argv.Length()) { v8::Handle options = argv[2]->ToObject(); bool error; // separator if (options->Has(separatorKey)) { separator = ObjectToCharacter(options->Get(separatorKey), error); if (error) { return scope.Close(v8::ThrowException(v8::String::New(".separator must be a character"))); } } // quote if (options->Has(quoteKey)) { quote = ObjectToCharacter(options->Get(quoteKey), error); if (error) { return scope.Close(v8::ThrowException(v8::String::New(".quote must be a character"))); } } } // read and convert int fd = open(*filename, O_RDONLY); if (fd < 0) { return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); } TRI_csv_parser_t parser; TRI_InitCsvParser(&parser, ProcessCsvBegin, ProcessCsvAdd, ProcessCsvEnd); parser._separator = separator; parser._quote = quote; parser._dataEnd = &cb; v8::Handle array; parser._dataBegin = &array; char buffer[10240]; while (true) { v8::HandleScope scope; ssize_t n = read(fd, buffer, sizeof(buffer)); if (n < 0) { TRI_DestroyCsvParser(&parser); return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); } else if (n == 0) { TRI_DestroyCsvParser(&parser); break; } TRI_ParseCsvString2(&parser, buffer, n); } return scope.Close(v8::Undefined()); } //////////////////////////////////////////////////////////////////////////////// /// @brief processes a JSON file //////////////////////////////////////////////////////////////////////////////// static v8::Handle ProcessJsonFileCall (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 2) { return scope.Close(v8::ThrowException(v8::String::New("usage: processJsonFile(, )"))); } // extract the filename v8::String::Utf8Value filename(argv[0]); if (*filename == 0) { return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); } // extract the callback v8::Handle cb = v8::Handle::Cast(argv[1]); // read and convert string line; ifstream file(*filename); if (file.is_open()) { size_t row = 0; while (file.good()) { v8::HandleScope scope; getline(file, line); char const* ptr = line.c_str(); char const* end = ptr + line.length(); while (ptr < end && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r')) { ++ptr; } if (ptr == end) { continue; } char* error; v8::Handle object = TRI_FromJsonString(line.c_str(), &error); if (object->IsUndefined()) { v8::Handle msg = v8::String::New(error); TRI_FreeString(error); return scope.Close(v8::ThrowException(msg)); } v8::Handle r = v8::Number::New(row); v8::Handle args[] = { object, r }; cb->Call(cb, 2, args); row++; } file.close(); } else { return scope.Close(v8::ThrowException(v8::String::New("cannot open file"))); } return scope.Close(v8::Undefined()); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the current time //////////////////////////////////////////////////////////////////////////////// static v8::Handle TimeCall (v8::Arguments const& argv) { v8::HandleScope scope; return scope.Close(v8::Number::New(TRI_microtime())); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Shell V8 Shell /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief converts an object to a string //////////////////////////////////////////////////////////////////////////////// string ObjectToString (v8::Handle value) { v8::String::Utf8Value utf8Value(value); if (*utf8Value == 0) { return ""; } else { return string(*utf8Value, utf8Value.length()); } } //////////////////////////////////////////////////////////////////////////////// /// @brief converts an object to a character //////////////////////////////////////////////////////////////////////////////// char ObjectToCharacter (v8::Handle value, bool& error) { error = false; if (! value->IsString() && ! value->IsStringObject()) { error = true; return '\0'; } v8::String::Utf8Value sep(value->ToString()); if (*sep == 0 || sep.length() != 1) { error = true; return '\0'; } return (*sep)[0]; } //////////////////////////////////////////////////////////////////////////////// /// @brief converts an object to a double //////////////////////////////////////////////////////////////////////////////// double ObjectToDouble (v8::Handle value) { if (value->IsNumber()) { return value->ToNumber()->Value(); } if (value->IsNumberObject()) { v8::Handle no = v8::Handle::Cast(value); return no->NumberValue(); } return 0.0; } //////////////////////////////////////////////////////////////////////////////// /// @brief converts an object to a double //////////////////////////////////////////////////////////////////////////////// double ObjectToDouble (v8::Handle value, bool& error) { error = false; if (value->IsNumber()) { return value->ToNumber()->Value(); } if (value->IsNumberObject()) { v8::Handle no = v8::Handle::Cast(value); return no->NumberValue(); } error = true; return 0.0; } //////////////////////////////////////////////////////////////////////////////// /// @brief stores the V8 shell functions inside the global variable //////////////////////////////////////////////////////////////////////////////// void InitV8Shell (v8::Handle global) { // create the global functions global->Set(v8::String::New("logLevel"), v8::FunctionTemplate::New(LogLevelCall), v8::ReadOnly); global->Set(v8::String::New("output"), v8::FunctionTemplate::New(OutputCall), v8::ReadOnly); global->Set(v8::String::New("print"), v8::FunctionTemplate::New(PrintCall), v8::ReadOnly); global->Set(v8::String::New("processCsvFile"), v8::FunctionTemplate::New(ProcessCsvFileCall), v8::ReadOnly); global->Set(v8::String::New("processJsonFile"), v8::FunctionTemplate::New(ProcessJsonFileCall), v8::ReadOnly); global->Set(v8::String::New("fromJson"), v8::FunctionTemplate::New(FromJsonCall), v8::ReadOnly); global->Set(v8::String::New("time"), v8::FunctionTemplate::New(TimeCall), v8::ReadOnly); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\)" // End: