From 29baa939789fa3ee637a70d0a47687399a3ad020 Mon Sep 17 00:00:00 2001 From: Christopher Jones Date: Tue, 5 Nov 2019 10:54:10 +1100 Subject: [PATCH] Eliminate a JavaScript memory leak --- CHANGELOG.md | 7 +++ lib/connection.js | 43 +++++++++---- lib/dbObject.js | 140 +++++++++++++++++++++-------------------- src/njsConnection.c | 5 +- src/njsDbObject.c | 149 +++++++++++++++++--------------------------- src/njsModule.c | 3 + src/njsModule.h | 1 + src/njsOracleDb.c | 10 ++- 8 files changed, 178 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeeb77fc..92dbf6d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## node-oracledb v4.0.2 (DD Mon YYYY) + +**This release is under development** + +- Fixed a JavaScript memory leak when getting Oracle Database named type + information, such as with `getDbObjectClass()`. + ## node-oracledb v4.0.1 (19 Aug 2019) - Fixed a regression causing a segfault when setting `oracledb.connectionClass` diff --git a/lib/connection.js b/lib/connection.js index 42e38fec..d5e282f0 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -291,6 +291,10 @@ function close(a1, a2) { self._close(options, function(err) { if (!err) { + for (const cls of Object.values(this._dbObjectClasses)) { + cls.prototype.constructor = Object; + cls.prototype = null; + } self.emit('_after_close'); } @@ -376,6 +380,31 @@ function unsubscribe(name, cb) { self._unsubscribe.call(self, name, cb); } +// build a database object class +function buildDbObjectClass(schema, name, fqn) { + const DbObject = function(initialValue) { + if (this.isCollection) { + const proxy = new Proxy(this, BaseDbObject._collectionProxyHandler); + if (initialValue !== undefined) { + for (let i = 0; i < initialValue.length; i++) { + this.append(initialValue[i]); + } + } + return proxy; + } else if (initialValue !== undefined) { + Object.assign(this, initialValue) + } + } + DbObject.prototype = Object.create(BaseDbObject.prototype); + DbObject.prototype.constructor = DbObject; + DbObject.prototype.schema = schema; + DbObject.prototype.name = name; + DbObject.prototype.fqn = fqn; + DbObject.toString = function() { + return 'DbObjectClass [' + fqn + ']'; + }; + return DbObject; +} // define class class Connection extends EventEmitter { @@ -409,18 +438,8 @@ class Connection extends EventEmitter { const fqn = `${schema}.${name}`; let cls = this._dbObjectClasses[fqn]; if (!cls) { - class DbObject extends BaseDbObject { - get [Symbol.toStringTag]() { - return fqn; - } - } - this._dbObjectClasses[fqn] = cls = DbObject; - cls.prototype.schema = schema; - cls.prototype.name = name; - cls.prototype.fqn = fqn; - cls.toString = function() { - return 'DbObjectClass [' + fqn + ']'; - }; + cls = buildDbObjectClass(schema, name, fqn); + this._dbObjectClasses[fqn] = cls; } return cls; } diff --git a/lib/dbObject.js b/lib/dbObject.js index a2f592f6..d33bce03 100644 --- a/lib/dbObject.js +++ b/lib/dbObject.js @@ -21,8 +21,78 @@ const util = require('util'); +// define base database object class; instances of this class are never +// instantiated; instead, classes subclassed from this one will be +// instantiated; a cache of these classes are maintained on each connection +class BaseDbObject { + + // extend class with promisified functions + _extend(oracledb) { + this._oracledb = oracledb; + } + + // initialize object with value + _initialize(initialValue) { + if (this.isCollection) { + for (let i = 0; i < initialValue.length; i++) { + this.append(initialValue[i]); + } + } else { + Object.assign(this, initialValue); + } + } + + // return as a plain object + _toPojo() { + if (this.isCollection) { + return this.getValues(); + } + const result = {}; + for (let name in this.attributes) { + let value = this[name]; + if (value instanceof BaseDbObject) { + value = value._toPojo(); + } + result[name] = value; + } + return result; + } + + // custom inspection routine + [util.inspect.custom](depth, options) { + return '[' + this.fqn + '] ' + util.inspect(this._toPojo(), options); + } + + [Symbol.iterator]() { + if (this.isCollection) { + const values = this.getValues(); + return values[Symbol.iterator](); + } + throw TypeError("obj is not iterable"); + } + + [Symbol.toPrimitive](hint) { + switch (hint) { + case 'number': + return NaN; + default: + return '[' + this.fqn + '] ' + util.inspect(this._toPojo(), {}); + } + } + + get [Symbol.toStringTag]() { + return this.fqn; + } + + toJSON() { + return this._toPojo(); + } + +} + + // define proxy handler used for collections -const collectionProxyHandler = { +BaseDbObject._collectionProxyHandler = { deleteProperty(target, prop) { if (typeof prop === 'string') { @@ -70,73 +140,5 @@ const collectionProxyHandler = { }; -// define base database object class; instances of this class are never -// instantiated; instead, classes subclassed from this one will be -// instantiated; a cache of these classes are maintained on each connection -class BaseDbObject { - - constructor(initialValue) { - if (this.isCollection) { - const proxy = new Proxy(this, collectionProxyHandler); - if (initialValue) { - for (let i = 0; i < initialValue.length; i++) { - this.append(initialValue[i]); - } - } - return proxy; - } else if (initialValue) { - Object.assign(this, initialValue); - } - } - - // extend class with promisified functions - _extend(oracledb) { - this._oracledb = oracledb; - } - - // return as a plain object - _toPojo() { - if (this.isCollection) { - return this.getValues(); - } - const result = {}; - for (let name in this.attributes) { - let value = this[name]; - if (value instanceof BaseDbObject) { - value = value._toPojo(); - } - result[name] = value; - } - return result; - } - - // custom inspection routine - [util.inspect.custom](depth, options) { - return '[' + this.fqn + '] ' + util.inspect(this._toPojo(), options); - } - - [Symbol.iterator]() { - if (this.isCollection) { - const values = this.getValues(); - return values[Symbol.iterator](); - } - throw TypeError("obj is not iterable"); - } - - [Symbol.toPrimitive](hint) { - switch (hint) { - case 'number': - return NaN; - default: - return '[' + this.fqn + '] ' + util.inspect(this._toPojo(), {}); - } - } - - toJSON() { - return this._toPojo(); - } - -} - module.exports = BaseDbObject; diff --git a/src/njsConnection.c b/src/njsConnection.c index 0436a4d4..38a8bb17 100644 --- a/src/njsConnection.c +++ b/src/njsConnection.c @@ -2350,8 +2350,9 @@ static bool njsConnection_scanExecuteBindUnit(njsBaton *baton, NJS_CHECK_NAPI(env, napi_get_named_property(env, args[1], "type", &value)); NJS_CHECK_NAPI(env, napi_typeof(env, value, &valueType)); if (valueType == napi_function) { - NJS_CHECK_NAPI(env, napi_get_prototype(env, value, &prototype)) - NJS_CHECK_NAPI(env, napi_strict_equals(env, prototype, + NJS_CHECK_NAPI(env, napi_get_named_property(env, value, "prototype", + &prototype)) + NJS_CHECK_NAPI(env, napi_instanceof(env, prototype, baton->jsBaseDbObjectConstructor, &check)) if (!check) return njsBaton_setError(baton, errInvalidBindDataType, 2); diff --git a/src/njsDbObject.c b/src/njsDbObject.c index f8083056..36f38719 100644 --- a/src/njsDbObject.c +++ b/src/njsDbObject.c @@ -52,9 +52,10 @@ static NJS_NAPI_FINALIZE(njsDbObject_finalize); static NJS_NAPI_FINALIZE(njsDbObjectType_finalize); // properties for collections -static const napi_property_descriptor njsCollectionProperties[] = { +static const napi_property_descriptor njsClassProperties[] = { { "append", NULL, njsDbObject_append, NULL, NULL, NULL, napi_default, NULL }, + { "copy", NULL, njsDbObject_copy, NULL, NULL, NULL, napi_default, NULL }, { "deleteElement", NULL, njsDbObject_deleteElement, NULL, NULL, NULL, napi_default, NULL }, { "getElement", NULL, njsDbObject_getElement, NULL, NULL, NULL, @@ -81,6 +82,12 @@ static const napi_property_descriptor njsCollectionProperties[] = { { NULL, NULL, NULL, NULL, NULL, NULL, napi_default, NULL } }; +// class definition +const njsClassDef njsClassDefBaseDbObject = { + "BaseDbObject", sizeof(njsDbObject), njsDbObject_finalize, + njsClassProperties, NULL, false +}; + // other methods used internally static bool njsDbObject_getAttrValueHelper(napi_env env, napi_callback_info info, napi_value *value); @@ -96,8 +103,8 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env, napi_value value, dpiNativeTypeNum *nativeTypeNum, dpiData *data, char **strBuffer, njsDbObjectAttr *attr); static bool njsDbObject_validateArgs(napi_env env, napi_callback_info info, - size_t numArgs, napi_value *args, njsDbObjectType **objType, - njsDbObjectAttr **attr, njsDbObject **obj); + size_t numArgs, napi_value *args, njsDbObjectAttr **attr, + njsDbObject **obj); static bool njsDbObjectType_populate(njsDbObjectType *objType, dpiObjectType *objectTypeHandle, napi_env env, napi_value jsObjectType, dpiObjectTypeInfo *info, njsBaton *baton); @@ -113,7 +120,6 @@ static bool njsDbObject_wrap(napi_env env, napi_value value, //----------------------------------------------------------------------------- static napi_value njsDbObject_append(napi_env env, napi_callback_info info) { - njsDbObjectType *objType = NULL; dpiNativeTypeNum nativeTypeNum; napi_value value; char *str = NULL; @@ -121,9 +127,9 @@ static napi_value njsDbObject_append(napi_env env, napi_callback_info info) dpiData data; int status; - if (!njsDbObject_validateArgs(env, info, 1, &value, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, &value, NULL, &obj)) return NULL; - nativeTypeNum = objType->elementTypeInfo.nativeTypeNum; + nativeTypeNum = obj->type->elementTypeInfo.nativeTypeNum; if (!njsDbObject_transformToOracle(obj, env, value, &nativeTypeNum, &data, &str, NULL)) return NULL; @@ -131,7 +137,7 @@ static napi_value njsDbObject_append(napi_env env, napi_callback_info info) if (str) free(str); if (status < 0) - njsUtils_throwErrorDPI(env, objType->oracleDb); + njsUtils_throwErrorDPI(env, obj->type->oracleDb); return NULL; } @@ -180,12 +186,11 @@ static napi_value njsDbObject_copy(napi_env env, napi_callback_info info) static napi_value njsDbObject_deleteElement(napi_env env, napi_callback_info info) { - njsDbObjectType *objType = NULL; napi_value args[1]; njsDbObject *obj; int32_t index; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; @@ -240,7 +245,7 @@ static bool njsDbObject_getAttrValueHelper(napi_env env, njsDbObject *obj; dpiData data; - if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &attr, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, &attr, &obj)) return false; if (dpiObject_getAttributeValue(obj->handle, attr->handle, attr->typeInfo.nativeTypeNum, &data) < 0) @@ -257,21 +262,20 @@ static bool njsDbObject_getAttrValueHelper(napi_env env, static napi_value njsDbObject_getElement(napi_env env, napi_callback_info info) { napi_value args[1], value = NULL; - njsDbObjectType *objType = NULL; njsDbObject *obj; int32_t index; dpiData data; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; if (dpiObject_getElementValueByIndex(obj->handle, index, - objType->elementTypeInfo.nativeTypeNum, &data) < 0 ) { + obj->type->elementTypeInfo.nativeTypeNum, &data) < 0 ) { njsUtils_throwErrorDPI(env, obj->type->oracleDb); return NULL; } - if (!njsDbObject_transformFromOracle(obj, env, &objType->elementTypeInfo, + if (!njsDbObject_transformFromOracle(obj, env, &obj->type->elementTypeInfo, &data, &value, NULL)) return NULL; return value; @@ -315,13 +319,12 @@ bool njsDbObject_getInstance(njsOracleDb *oracleDb, napi_env env, static napi_value njsDbObject_getFirstIndex(napi_env env, napi_callback_info info) { - njsDbObjectType *objType; napi_value value = NULL; njsDbObject *obj; int32_t index; int exists; - if (!njsDbObject_validateArgs(env, info, 0, NULL, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &obj)) return NULL; if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) { njsUtils_throwErrorDPI(env, obj->type->oracleDb); @@ -356,14 +359,13 @@ static napi_value njsDbObject_getKeys(napi_env env, napi_callback_info info) static bool njsDbObject_getKeysHelper(napi_env env, napi_callback_info info, napi_value *returnValue) { - njsDbObjectType *objType = NULL; int32_t index, exists, size; napi_value arr, temp; uint32_t arrayPos; njsDbObject *obj; // get object instance from caller - if (!njsDbObject_validateArgs(env, info, 0, NULL, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &obj)) return false; // determine the size of the collection and create an array of that length @@ -394,13 +396,12 @@ static bool njsDbObject_getKeysHelper(napi_env env, napi_callback_info info, static napi_value njsDbObject_getLastIndex(napi_env env, napi_callback_info info) { - njsDbObjectType *objType; napi_value value = NULL; njsDbObject *obj; int32_t index; int exists; - if (!njsDbObject_validateArgs(env, info, 0, NULL, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &obj)) return NULL; if (dpiObject_getLastIndex(obj->handle, &index, &exists) < 0) { njsUtils_throwErrorDPI(env, obj->type->oracleDb); @@ -420,11 +421,10 @@ static napi_value njsDbObject_getLastIndex(napi_env env, //----------------------------------------------------------------------------- static napi_value njsDbObject_getLength(napi_env env, napi_callback_info info) { - njsDbObjectType *objType; njsDbObject *obj; int32_t size; - if (!njsDbObject_validateArgs(env, info, 0, NULL, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &obj)) return NULL; if (dpiObject_getSize(obj->handle, &size) < 0) { njsUtils_throwErrorDPI(env, obj->type->oracleDb); @@ -442,12 +442,11 @@ static napi_value njsDbObject_getNextIndex(napi_env env, napi_callback_info info) { napi_value args[1], value = NULL; - njsDbObjectType *objType; njsDbObject *obj; int32_t index; int exists; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; @@ -471,12 +470,11 @@ static napi_value njsDbObject_getPrevIndex(napi_env env, napi_callback_info info) { napi_value args[1], value = NULL; - njsDbObjectType *objType; njsDbObject *obj; int32_t index; int exists; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; @@ -572,7 +570,6 @@ static napi_value njsDbObject_getValues(napi_env env, napi_callback_info info) static bool njsDbObject_getValuesHelper(napi_env env, napi_callback_info info, napi_value *returnValue) { - njsDbObjectType *objType = NULL; int32_t index, exists, size; napi_value arr, temp; uint32_t arrayPos; @@ -580,7 +577,7 @@ static bool njsDbObject_getValuesHelper(napi_env env, napi_callback_info info, dpiData data; // get object instance from caller - if (!njsDbObject_validateArgs(env, info, 0, NULL, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 0, NULL, NULL, &obj)) return false; // determine the size of the collection and create an array of that length @@ -595,10 +592,10 @@ static bool njsDbObject_getValuesHelper(napi_env env, napi_callback_info info, return njsUtils_throwErrorDPI(env, obj->type->oracleDb); while (exists) { if (dpiObject_getElementValueByIndex(obj->handle, index, - objType->elementTypeInfo.nativeTypeNum, &data) < 0) + obj->type->elementTypeInfo.nativeTypeNum, &data) < 0) return njsUtils_throwErrorDPI(env, obj->type->oracleDb); if (!njsDbObject_transformFromOracle(obj, env, - &objType->elementTypeInfo, &data, &temp, NULL)) + &obj->type->elementTypeInfo, &data, &temp, NULL)) return false; NJS_CHECK_NAPI(env, napi_set_element(env, arr, arrayPos++, temp)) if (dpiObject_getNextIndex(obj->handle, index, &index, &exists) < 0) @@ -616,13 +613,12 @@ static bool njsDbObject_getValuesHelper(napi_env env, napi_callback_info info, //----------------------------------------------------------------------------- static napi_value njsDbObject_hasElement(napi_env env, napi_callback_info info) { - njsDbObjectType *objType = NULL; napi_value args[1], result; njsDbObject *obj; int32_t index; int exists; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; @@ -692,7 +688,7 @@ static bool njsDbObject_setAttrValueHelper(napi_env env, int status; // get object instance and validate number of arguments - if (!njsDbObject_validateArgs(env, info, 1, &value, NULL, &attr, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, &value, &attr, &obj)) return false; // transform to value required by ODPI-C @@ -719,7 +715,6 @@ static bool njsDbObject_setAttrValueHelper(napi_env env, //----------------------------------------------------------------------------- static napi_value njsDbObject_setElement(napi_env env, napi_callback_info info) { - njsDbObjectType *objType = NULL; dpiNativeTypeNum nativeTypeNum; napi_value args[2]; njsDbObject *obj; @@ -728,11 +723,11 @@ static napi_value njsDbObject_setElement(napi_env env, napi_callback_info info) dpiData data; int status; - if (!njsDbObject_validateArgs(env, info, 2, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 2, args, NULL, &obj)) return NULL; if (!njsUtils_getIntArg(env, args, 0, &index)) return NULL; - nativeTypeNum = objType->elementTypeInfo.nativeTypeNum; + nativeTypeNum = obj->type->elementTypeInfo.nativeTypeNum; if (!njsDbObject_transformToOracle(obj, env, args[1], &nativeTypeNum, &data, &str, NULL)) return NULL; @@ -974,12 +969,11 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env, //----------------------------------------------------------------------------- static napi_value njsDbObject_trim(napi_env env, napi_callback_info info) { - njsDbObjectType *objType = NULL; uint32_t numToTrim; napi_value args[1]; njsDbObject *obj; - if (!njsDbObject_validateArgs(env, info, 1, args, &objType, NULL, &obj)) + if (!njsDbObject_validateArgs(env, info, 1, args, NULL, &obj)) return NULL; if (!njsUtils_getUnsignedIntArg(env, args, 0, &numToTrim)) return NULL; @@ -997,8 +991,8 @@ static napi_value njsDbObject_trim(napi_env env, napi_callback_info info) // all. In that case, a new instance is created and associated with the object. //----------------------------------------------------------------------------- static bool njsDbObject_validateArgs(napi_env env, napi_callback_info info, - size_t numArgs, napi_value *args, njsDbObjectType **objType, - njsDbObjectAttr **attr, njsDbObject **obj) + size_t numArgs, napi_value *args, njsDbObjectAttr **attr, + njsDbObject **obj) { njsOracleDb *oracleDb; napi_value thisArg; @@ -1012,14 +1006,13 @@ static bool njsDbObject_validateArgs(napi_env env, napi_callback_info info, if (actualArgs != numArgs) return njsUtils_throwError(env, errInvalidNumberOfParameters); - // data will either be an object type or an attribute, determined by which - // pointer is not NULL + // data will either be an attribute or a pointer to the global njsOracleDb + // instance if (attr) { *attr = (njsDbObjectAttr*) data; oracleDb = (*attr)->oracleDb; } else { - *objType = (njsDbObjectType*) data; - oracleDb = (*objType)->oracleDb; + oracleDb = (njsOracleDb*) data; } return njsDbObject_getInstance(oracleDb, env, thisArg, obj); @@ -1105,25 +1098,17 @@ static void njsDbObjectType_finalize(napi_env env, void *finalizeData, //----------------------------------------------------------------------------- // njsDbObjectType_getFromClass() -// Acquires the object type from a class. The object type instance is stored -// on the prototype so a new instance needs to be created, the prototype -// acquired from that new instance, and then the object type instance -// unwrapped. There is a slight complication, however, in that collections are -// proxied, so we need to acquire the actual target first. +// Acquires the object type from a class by acquiring the prototype and then +// unwrapping it to find the object type instance. //----------------------------------------------------------------------------- bool njsDbObjectType_getFromClass(napi_env env, napi_value cls, njsDbObjectType **objType) { - napi_value prototype, temp, tempObj; + napi_value prototype; - NJS_CHECK_NAPI(env, napi_new_instance(env, cls, 0, NULL, &tempObj)) - NJS_CHECK_NAPI(env, napi_get_prototype(env, tempObj, &prototype)) - if (napi_unwrap(env, prototype, (void**) objType) != napi_ok) { - NJS_CHECK_NAPI(env, napi_get_named_property(env, tempObj, "_target", - &temp)) - NJS_CHECK_NAPI(env, napi_get_prototype(env, temp, &prototype)) - NJS_CHECK_NAPI(env, napi_unwrap(env, prototype, (void**) objType)) - } + NJS_CHECK_NAPI(env, napi_get_named_property(env, cls, "prototype", + &prototype)) + NJS_CHECK_NAPI(env, napi_unwrap(env, prototype, (void**) objType)) return true; } @@ -1139,9 +1124,9 @@ static bool njsDbObjectType_populate(njsDbObjectType *objType, { dpiObjectAttr **attrHandles; dpiObjectAttrInfo attrInfo; - size_t numProperties, p; napi_value attrs, temp; njsDbObjectAttr *attr; + size_t numProperties; napi_value element; uint16_t i; @@ -1178,28 +1163,9 @@ static bool njsDbObjectType_populate(njsDbObjectType *objType, } - // determine the number of descriptors to create; collections use a - // standard set of descriptors, whereas objects with attributes have one - // property created for each attribute; both types always have one extra - // descriptor to create a copy of an object - if (info->isCollection) { - for (numProperties = 0; - njsCollectionProperties[numProperties].utf8name; - numProperties++); - } else { - numProperties = info->numAttributes; - } - numProperties++; - objType->descriptors = calloc(numProperties, - sizeof(napi_property_descriptor)); - if (!objType->descriptors) - return njsBaton_setError(baton, errInsufficientMemory); - objType->descriptors[0].utf8name = "copy"; - objType->descriptors[0].method = njsDbObject_copy; - objType->descriptors[0].data = objType; - // process collections object types if (info->isCollection) { + numProperties = 0; if (!njsDbObjectType_populateTypeInfo(&objType->elementTypeInfo, baton, env, &info->elementTypeInfo)) return false; @@ -1207,13 +1173,14 @@ static bool njsDbObjectType_populate(njsDbObjectType *objType, info->elementTypeInfo.oracleTypeNum, objType->elementTypeInfo.objectType)) return false; - memcpy(&objType->descriptors[1], njsCollectionProperties, - (numProperties - 1) * sizeof(napi_property_descriptor)); - for (p = 1; p < numProperties; p++) - objType->descriptors[p].data = objType; // process object types with attributes } else { + numProperties = info->numAttributes; + objType->descriptors = calloc(numProperties, + sizeof(napi_property_descriptor)); + if (!objType->descriptors) + return njsBaton_setError(baton, errInsufficientMemory); NJS_CHECK_NAPI(env, napi_create_object(env, &attrs)) for (i = 0; i < info->numAttributes; i++) { attr = &objType->attributes[i]; @@ -1232,20 +1199,20 @@ static bool njsDbObjectType_populate(njsDbObjectType *objType, return false; NJS_CHECK_NAPI(env, napi_create_string_utf8(env, attrInfo.name, attrInfo.nameLength, &temp)) - objType->descriptors[i + 1].name = temp; - objType->descriptors[i + 1].getter = njsDbObject_getAttrValue; - objType->descriptors[i + 1].setter = njsDbObject_setAttrValue; - objType->descriptors[i + 1].data = &objType->attributes[i]; + objType->descriptors[i].name = temp; + objType->descriptors[i].getter = njsDbObject_getAttrValue; + objType->descriptors[i].setter = njsDbObject_setAttrValue; + objType->descriptors[i].data = &objType->attributes[i]; NJS_CHECK_NAPI(env, napi_set_property(env, attrs, temp, element)) } NJS_CHECK_NAPI(env, napi_set_named_property(env, jsObjectType, "attributes", attrs)) + if (numProperties > 0) { + NJS_CHECK_NAPI(env, napi_define_properties(env, jsObjectType, + numProperties, objType->descriptors)) + } } - // define properties - NJS_CHECK_NAPI(env, napi_define_properties(env, jsObjectType, - numProperties, objType->descriptors)) - // keep a reference to the constructor NJS_CHECK_NAPI(env, napi_get_named_property(env, jsObjectType, "constructor", &temp)) diff --git a/src/njsModule.c b/src/njsModule.c index 9e4a6516..8599d38c 100644 --- a/src/njsModule.c +++ b/src/njsModule.c @@ -68,6 +68,9 @@ static napi_value njsModule_externalInit(napi_env env, napi_callback_info info) if (!njsOracleDb_prepareClass(oracleDb, env, instance, &njsClassDefAqQueue, &oracleDb->jsAqQueueConstructor)) return NULL; + if (!njsOracleDb_prepareClass(oracleDb, env, instance, + &njsClassDefBaseDbObject, &oracleDb->jsBaseDbObjectConstructor)) + return NULL; if (!njsOracleDb_prepareClass(oracleDb, env, instance, &njsClassDefConnection, &oracleDb->jsConnectionConstructor)) return NULL; diff --git a/src/njsModule.h b/src/njsModule.h index 468a0da2..b3beeaf9 100644 --- a/src/njsModule.h +++ b/src/njsModule.h @@ -253,6 +253,7 @@ extern const njsClassDef njsClassDefAqDeqOptions; extern const njsClassDef njsClassDefAqEnqOptions; extern const njsClassDef njsClassDefAqMessage; extern const njsClassDef njsClassDefAqQueue; +extern const njsClassDef njsClassDefBaseDbObject; extern const njsClassDef njsClassDefConnection; extern const njsClassDef njsClassDefLob; extern const njsClassDef njsClassDefOracleDb; diff --git a/src/njsOracleDb.c b/src/njsOracleDb.c index ab7c5de1..89a9f106 100644 --- a/src/njsOracleDb.c +++ b/src/njsOracleDb.c @@ -1122,12 +1122,6 @@ bool njsOracleDb_new(napi_env env, napi_value instanceObj, return njsUtils_genericThrowError(env); } - // keep a reference to the base database object class - NJS_CHECK_NAPI(env, napi_get_named_property(env, instanceObj, - "BaseDbObject", &temp)) - NJS_CHECK_NAPI(env, napi_create_reference(env, temp, 1, - &oracleDb->jsBaseDbObjectConstructor)) - // create object for storing subscriptions NJS_CHECK_NAPI(env, napi_create_object(env, &temp)) NJS_CHECK_NAPI(env, napi_create_reference(env, temp, 1, @@ -1179,6 +1173,10 @@ bool njsOracleDb_prepareClass(njsOracleDb *oracleDb, napi_env env, return false; } + // store the instance on each of the properties as a convenience + for (i = 0; i < numProperties; i++) + allProperties[i].data = oracleDb; + // populate the properties memcpy(allProperties, classDef->properties, sizeof(napi_property_descriptor) * numBaseProperties);