8000 slightly faster V8 => JSON conversion in AQL · cloud-coders/arangodb@de9bc61 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit de9bc61

Browse files
committed
slightly faster V8 => JSON conversion in AQL
1 parent 6617810 commit de9bc61

File tree

6 files changed

+234
-6
lines changed

6 files changed

+234
-6
lines changed

arangod/Aql/Executor.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,21 @@ V8Expression* Executor::generateExpression (AstNode const* node) {
293293
// exit early if an error occurred
294294
HandleV8Error(tryCatch, func);
295295

296-
return new V8Expression(isolate, v8::Handle<v8::Function>::Cast(func));
296+
// a "simple" expression here is any expression that will only return non-cyclic
297+
// data and will not return any special JavaScript types such as Date, RegExp or
298+
// Function
299+
// as we know that all built-in AQL functions are simple but do not know anything
300+
// about user-defined functions, we use the following approximation:
301+
// - all user-defined functions are marked as potentially throwing
302+
// - some of the internal functions are marked as potentially throwing
303+
// - if an expression is marked as potentially throwing, it may contain a user-
304+
// defined function (which may be non-simple) and is thus considered non-simple
305+
// - if an expression is marked as not throwing, it will not contain a user-
306+
// defined function but only internal AQL functions (which are simple) so the
307+
// whole expression is considered simple
308+
bool isSimple = (! node->canThrow());
309+
310+
return new V8Expression(isolate, v8::Handle<v8::Function>::Cast(func), isSimple);
297311
}
298312

299313
////////////////////////////////////////////////////////////////////////////////

arangod/Aql/Executor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ namespace triagens {
8282
/// @brief generates an expression execution object
8383
////////////////////////////////////////////////////////////////////////////////
8484

85-
V8Expression* generateExpression (AstNode const*);
85+
V8Expression* generateExpression (AstNode const*);
8686

8787
////////////////////////////////////////////////////////////////////////////////
8888
/// @brief executes an expression directly

arangod/Aql/V8Expression.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ using namespace triagens::aql;
4646
////////////////////////////////////////////////////////////////////////////////
4747

4848
V8Expression::V8Expression (v8::Isolate* isolate,
49-
v8::Handle<v8::Function> func)
49+
v8::Handle<v8::Function> func,
50+
bool isSimple)
5051
: isolate(isolate),
51-
_func() {
52+
_func(),
53+
_isSimple(isSimple) {
5254

5355
_func.Reset(isolate, func);
5456
}
@@ -150,7 +152,12 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
150152
}
151153
else {
152154
// expression had a result. convert it to JSON
153-
json.reset(TRI_ObjectToJson(isolate, result));
155+
if (_isSimple) {
156+
json.reset(TRI_ObjectToJsonSimple(isolate, result));
157+
}
158+
else {
159+
json.reset(TRI_ObjectToJson(isolate, result));
160+
}
154161
}
155162

156163
if (json.get() == nullptr) {

arangod/Aql/V8Expression.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ namespace triagens {
5656
////////////////////////////////////////////////////////////////////////////////
5757

5858
V8Expression (v8::Isolate*,
59-
v8::Handle<v8::Function>);
59+
v8::Handle<v8::Function>,
60+
bool);
6061

6162
////////////////////////////////////////////////////////////////////////////////
6263
/// @brief destroy the v8 expression
@@ -115,6 +116,14 @@ namespace triagens {
115116

116117
std::unordered_map<Variable const*, std::unordered_set<std::string>> _attributeRestrictions;
117118

119+
////////////////////////////////////////////////////////////////////////////////
120+
/// @brief whether or not the expression is simple. simple in this case means
121+
/// that the expression result will always contain non-cyclic data and no
122+
/// special JavaScript types such as Date, RegExp, Function etc.
123+
////////////////////////////////////////////////////////////////////////////////
124+
125+
bool const _isSimple;
126+
118127
};
119128

120129
}

lib/V8/v8-conv.cpp

Lines changed: 189 additions & 0 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,195 @@ TRI_json_t* TRI_ObjectToJson (v8::Isolate* isolate,
19501950
return json;
19511951
}
19521952

1953+
////////////////////////////////////////////////////////////////////////////////
1954+
/// @brief convert a V8 value to a TRI_json_t value
1955+
////////////////////////////////////////////////////////////////////////////////
1956+
1957+
static int ObjectToJsonSimple (v8::Isolate* isolate,
1958+
TRI_json_t* result,
1959+
v8::Handle<v8::Value> const parameter) {
1960+
v8::HandleScope scope(isolate);
1961+
1962+
if (parameter->IsNull()) {
1963+
TRI_InitNullJson(result);
1964+
return TRI_ERROR_NO_ERROR;
1965+
}
1966+
1967+
if (parameter->IsBoolean()) {
1968+
v8::Handle<v8::Boolean> booleanParameter = parameter->ToBoolean();
1969+
TRI_InitBooleanJson(result, booleanParameter->Value());
1970+
return TRI_ERROR_NO_ERROR;
1971+
}
1972+
1973+
if (parameter->IsNumber()) {
1974+
v8::Handle<v8::Number> numberParameter = parameter->ToNumber();
1975+
TRI_InitNumberJson(result, numberParameter->Value());
1976+
return TRI_ERROR_NO_ERROR;
1977+
}
1978+
1979+
if (parameter->IsString()) {
1980+
v8::Handle<v8::String> stringParameter = parameter->ToString();
1981+
TRI_Utf8ValueNFC str(TRI_UNKNOWN_MEM_ZONE, stringParameter);
1982+
1983+
if (*str == nullptr) {
1984+
TRI_InitNullJson(result);
1985+
return TRI_ERROR_OUT_OF_MEMORY;
1986+
}
1987+
1988+
// this passes ownership for the utf8 string to the JSON object
1989+
TRI_InitStringJson(result, str.steal(), str.length());
1990+
return TRI_ERROR_NO_ERROR;
1991+
}
1992+
1993+
if (parameter->IsArray()) {
1994+
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(parameter);
1995+
uint32_t const n = array->Length();
1996+
1997+
// allocate the result array in one go
1998+
TRI_InitArrayJson(TRI_UNKNOWN_MEM_ZONE, result, static_cast<size_t>(n));
1999+
int res = TRI_ReserveVector(&result->_value._objects, static_cast<size_t>(n));
2000+
2001+
if (res != TRI_ERROR_NO_ERROR) {
2002+
// result array could not be allocated
2003+
TRI_InitNullJson(result);
2004+
return TRI_ERROR_OUT_OF_MEMORY;
2005+
}
2006+
2007+
for (uint32_t i = 0; i < n; ++i) {
2008+
// get address of next element
2009+
TRI_json_t* next = static_cast<TRI_json_t*>(TRI_NextVector(&result->_value._objects));
2010+
// the reserve call above made sure we could not have run out of memory
2011+
TRI_ASSERT_EXPENSIVE(next != nullptr);
2012+
2013+
res = ObjectToJsonSimple(isolate, next, array->Get(i));
2014+
2015+
if (res != TRI_ERROR_NO_ERROR) {
2016+
// to mimic behavior of previous ArangoDB versions, we need to silently ignore this error
2017+
// now return the element to the vector
2018+
TRI_ReturnVector(&result->_value._objects);
2019+
2020+
// a better solution would be:
2021+
// initialize the element at position, otherwise later cleanups may
2022+
// peek into uninitialized memory
2023+
// TRI_InitNullJson(next);
2024+
// return res;
2025+
}
2026+
}
2027+
2028+
return TRI_ERROR_NO_ERROR;
2029+
}
2030+
2031+
if (parameter->IsObject()) {
2032+
if (parameter->IsBooleanObject()) {
2033+
TRI_InitBooleanJson(result, v8::Handle<v8::BooleanObject>::Cast(parameter)->BooleanValue());
2034+
return TRI_ERROR_NO_ERROR;
2035+
}
2036+
2037+
if (parameter->IsNumberObject()) {
2038+
TRI_InitNumberJson(result, v8::Handle<v8::NumberObject>::Cast(parameter)->NumberValue());
2039+
return TRI_ERROR_NO_ERROR;
2040+
}
2041+
2042+
if (parameter->IsStringObject()) {
2043+
v8::Handle<v8::String> stringParameter(parameter->ToString());
2044+
TRI_Utf8ValueNFC str(TRI_UNKNOWN_MEM_ZONE, stringParameter);
2045+
2046+
if (*str == nullptr) {
2047+
TRI_InitNullJson(result);
2048+
return TRI_ERROR_OUT_OF_MEMORY;
2049+
}
2050+
2051+
// this passes ownership for the utf8 string to the JSON object
2052+
TRI_InitStringJson(result, str.steal(), str.length());
2053+
return TRI_ERROR_NO_ERROR;
2054+
}
2055+
2056+
v8::Handle<v8::Object> o = parameter->ToObject();
2057+
v8::Handle<v8::Array> names = o->GetOwnPropertyNames();
2058+
uint32_t const n = names->Length();
2059+
2060+
// allocate the result object buffer in one go
2061+
TRI_InitObjectJson(TRI_UNKNOWN_MEM_ZONE, result, static_cast<size_t>(n));
2062+
int res = TRI_ReserveVector(&result->_value._objects, static_cast<size_t>(n * 2)); // key + value
2063+
2064+
if (res != TRI_ERROR_NO_ERROR) {
2065+
// result object buffer could not be allocated
2066+
TRI_InitNullJson(result);
2067+
return TRI_ERROR_OUT_OF_MEMORY;
2068+
}
2069+
2070+
for (uint32_t i = 0; i < n; ++i) {
2071+
// process attribute name
2072+
v8::Handle<v8::Value> key = names->Get(i);
2073+
TRI_Utf8ValueNFC str(TRI_UNKNOWN_MEM_ZONE, key);
2074+
2075+
if (*str == nullptr) {
2076+
return TRI_ERROR_OUT_OF_MEMORY;
2077+
}
2078+
2079+
TRI_json_t* next = static_cast<TRI_json_t*>(TRI_NextVector(&result->_value._objects));
2080+
// the reserve call above made sure we could not have run out of memory
2081+
TRI_ASSERT_EXPENSIVE(next != nullptr);
2082+
2083+
// this passes ownership for the utf8 string to the JSON object
2084+
char* attributeName = str.steal();
2085+
TRI_InitStringJson(next, attributeName, str.length());
2086+
2087+
// process attribute value
2088+
next = static_cast<TRI_json_t*>(TRI_NextVector(&result->_value._objects));
2089+
// the reserve call above made sure we could not have run out of memory
2090+
TRI_ASSERT_EXPENSIVE(next != nullptr);
2091+
2092+
res = ObjectToJsonSimple(isolate, next, o->Get(key));
2093+
2094+
if (res != TRI_ERROR_NO_ERROR) {
2095+
// to mimic behavior of previous ArangoDB versions, we need to silently ignore this error
2096+
// now free the attributeName string and return the elements to the vector
2097+
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, attributeName);
2098+
TRI_ReturnVector(&result->_value._objects);
2099+
TRI_ReturnVector(&result->_value._objects);
2100+
2101+
// a better solution would be:
2102+
// initialize the element at position, otherwise later cleanups may
2103+
// peek into uninitialized memory
2104+
// TRI_InitNullJson(next);
2105+
// return res;
2106+
}
2107+
}
2108+
2109+
return TRI_ERROR_NO_ERROR;
2110+
}
2111+
2112+
TRI_InitNullJson(result);
2113+
return TRI_ERROR_BAD_PARAMETER;
2114+
}
2115+
2116+
////////////////////////////////////////////////////////////////////////////////
2117+
/// @brief convert a V8 value to a json_t value
2118+
/// this function assumes that the V8 object does not contain any cycles and
2119+
/// does not contain types such as Function, Date or RegExp
2120+
////////////////////////////////////////////////////////////////////////////////
2121+
2122+
TRI_json_t* TRI_ObjectToJsonSimple (v8::Isolate* isolate,
2123+
v8::Handle<v8::Value> const parameter) {
2124+
2125+
TRI_json_t* json = TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE);
2126+
2127+
if (json == nullptr) {
2128+
return nullptr;
2129+
}
2130+
2131+
int res = ObjectToJsonSimple(isolate, json, parameter);
2132+
2133+
if (res != TRI_ERROR_NO_ERROR) {
2134+
// some processing error occurred
2135+
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
2136+
return nullptr;
2137+
}
2138+
2139+
return json;
2140+
}
2141+
19532142
////////////////////////////////////////////////////////////////////////////////
19542143
/// @brief converts a V8 object to a string
19552144
////////////////////////////////////////////////////////////////////////////////

lib/V8/v8-conv.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ int TRI_FillShapedJsonV8Object (v8::Isolate* isolate,
112112
TRI_json_t* TRI_ObjectToJson (v8::Isolate*,
113113
v8::Handle<v8::Value> const);
114114

115+
////////////////////////////////////////////////////////////////////////////////
116+
/// @brief convert a V8 value to a json_t value
117+
/// this function assumes that the V8 object does not contain any cycles and
118+
/// does not contain types such as Function, Date or RegExp
119+
////////////////////////////////////////////////////////////////////////////////
120+
121+
TRI_json_t* TRI_ObjectToJsonSimple (v8::Isolate*,
122+
v8::Handle<v8::Value> const);
123+
115124
////////////////////////////////////////////////////////////////////////////////
116125
/// @brief converts an V8 object to a string
117126
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)
0