@doodad-js/xml
Version:
828 lines (732 loc) • 26.9 kB
JavaScript
// Copyright 2015-2018 Claude Petit, licensed under Apache License version 2.0
/* eslint camelcase: "off", id-match: "off" */ // Mixing C and JS
"use strict";
exports.add = function add(modules) {
modules = (modules || {});
modules['Doodad.Tools.Xml.Parsers.Libxml2'] = {
version: '5.2.0b',
dependencies: [
'Doodad.Tools.Xml',
'Doodad.Tools.Xml.Parsers.Libxml2.Loader',
],
create: function create(root, /*optional*/_options) {
//===================================
// Get namespaces
//===================================
const doodad = root.Doodad,
types = doodad.Types,
tools = doodad.Tools,
//namespaces = doodad.Namespaces,
io = doodad.IO,
ioMixIns = io.MixIns,
files = tools.Files,
xml = tools.Xml,
xmlParsers = xml.Parsers,
libxml2 = xmlParsers.Libxml2,
libxml2Loader = libxml2.Loader;
//===================================
// Internal
//===================================
const NULL = 0;
const __Internal__ = {
initialized: false,
createStrPtr: null,
baseDirectories: null,
};
//===================================
// Libxml2 Parser
//===================================
__Internal__.registerBaseDirectory = function registerBaseDirectory(schemaParserCtxt, url) {
if (!__Internal__.baseDirectories) {
__Internal__.baseDirectories = tools.nullObject();
};
const directory = url.set({file: ''});
const directoryStr = directory.toApiString();
if (types.has(__Internal__.baseDirectories, schemaParserCtxt)) {
const dirs = __Internal__.baseDirectories[schemaParserCtxt];
const len = dirs.length;
let found = false;
for (let i = 0; i < len; i++) {
const dir = dirs[i];
if (dir[0] === directoryStr) {
found = true;
break;
};
};
if (!found) {
__Internal__.baseDirectories[schemaParserCtxt].push( [directoryStr, directory] );
};
} else {
__Internal__.baseDirectories[schemaParserCtxt] = [[directoryStr, directory]];
};
};
__Internal__.unregisterBaseDirectories = function unregisterBaseDirectories(schemaParserCtxt) {
if (!__Internal__.baseDirectories || !types.has(__Internal__.baseDirectories, schemaParserCtxt)) {
throw new types.Error("Base directory not registered on schema parser '~0~'.", [schemaParserCtxt]);
};
delete __Internal__.baseDirectories[schemaParserCtxt];
};
__Internal__.getBaseDirectories = function getBaseDirectories(schemaParserCtxt) {
if (__Internal__.baseDirectories) {
return types.get(__Internal__.baseDirectories, schemaParserCtxt);
};
return undefined;
};
libxml2.REGISTER(types.ScriptAbortedError.$inherit(
/*typeProto*/
{
$TYPE_NAME: 'AbortError',
$TYPE_UUID: '68cbf932-9a3a-4649-87ec-9f5800d3261b',
[types.ConstructorSymbol](what) {
this.what = what;
return [1, "Script aborted : '~0~'.", [what]];
}
},
/*instanceProto*/
{
what: null,
}));
libxml2.ADD('init', function init() {
if (__Internal__.initialized) {
return;
};
const clibxml2 = libxml2Loader.get();
clibxml2.onExit = function onExit(status) {
tools.abortScript(status);
};
clibxml2.onAbort = function onAbort(what) {
throw new libxml2.AbortError(what);
};
clibxml2._xmlInitParser();
const matchFunc = clibxml2.addFunction(function matchFunc(filenameStrPtr) {
// Force selection of our handlers.
return 1;
}, 'ii');
const openFunc = clibxml2.addFunction(function openFunc(filenameStrPtr) {
// Disable file system access
return NULL;
}, 'ii');
const readFunc = clibxml2.addFunction(function readFunc(inputContextPtr, bufferPtr, bufferLen) {
// Disable file system access
return 0;
}, 'iiii');
const writeFunc = clibxml2.addFunction(function writeFunc(inputContextPtr, bufferPtr, bufferLen) {
// Disable file system access
return bufferLen;
}, 'iiii');
const closeFunc = clibxml2.addFunction(function closeFunc(inputContextPtr) {
// Disable file system access
return 0;
}, 'ii');
//const readExternalFunc = clibxml2.addFunction(function readExternalFunc(readContextPtr, bufferPtr, bufferLen) {
// types.DEBUGGER();
//}, 'viii');
//const closeExternalFunc = clibxml2.addFunction(function closeExternalFunc(readContextPtr) {
// clibxml2._free(readContextPtr);
// readContextPtr = NULL;
//}, 'vi');
//const externalLoader = clibxml2.addFunction(function externalLoader(urlStrPtr, idStrPtr, parserCtxtPtr) {
// const path = xsd.set({file: null}).combine(clibxml2.Pointer_stringify(urlStrPtr));
// const readContextPtr = clibxml2._malloc(....);
// const inputPtr = clibxml2._xmlCreateMyParserInput(parserCtxtPtr, readContextPtr, readExternalFunc, closeExternalFunc);
// return inputPtr;
//}, 'iiii');
const externalLoader = clibxml2.addFunction(function externalLoader(urlStrPtr, idStrPtr, parserCtxtPtr) {
// TODO: Read and store base directory from/to "userDataPtr" when "xmlSchemaNewParserCtxt" will accept a "userData" argument.
const url = files.parseLocation(clibxml2.Pointer_stringify(urlStrPtr));
const userDataPtr = clibxml2._xmlGetUserDataFromParserCtxt(parserCtxtPtr);
if (!userDataPtr) {
throw new types.Error("The 'libxml2' C library needs some modifications before its build.");
};
let content = null;
if (url.isRelative) {
const dirs = __Internal__.getBaseDirectories(userDataPtr),
len = dirs.length;
for (let i = 0; i < len; i++) {
const path = dirs[i][1].combine(url/*, {includePathInRoot: false}*/);
try {
content = files.readFileSync(path);
__Internal__.registerBaseDirectory(userDataPtr, path);
break;
} catch(ex) {
if (ex.code !== 'ENOENT') {
throw ex;
};
};
};
} else {
content = files.readFileSync(url);
__Internal__.registerBaseDirectory(userDataPtr, url);
};
//console.log(path.toApiString());
if (!content) {
return NULL;
};
let contentPtr = NULL;
//let filenamePtr = NULL;
try {
const contentLen = content.length;
contentPtr = clibxml2.allocate(content, 'i8', clibxml2.ALLOC_NORMAL);
content = null; // free memory
if (!contentPtr) {
throw new types.Error("Failed to allocate buffer file content.");
};
//filenamePtr = clibxml2.allocate(path.toApiString(), 'i8', clibxml2.ALLOC_NORMAL);
//if (!filenamePtr) {
// throw new types.Error("Failed to allocate buffer for file name.");
//};
const inputPtr = clibxml2._xmlCreateMyParserInput(parserCtxtPtr, contentPtr, contentLen/*, filenamePtr*/);
return inputPtr;
} catch (ex) {
throw ex;
} finally {
//if (filenamePtr) {
// clibxml2._free(filenamePtr);
// filenamePtr = NULL;
//};
if (contentPtr) {
clibxml2._free(contentPtr);
contentPtr = NULL;
};
};
}, 'iiii');
// Disable file system access
clibxml2._xmlCleanupInputCallbacks();
clibxml2._xmlCleanupOutputCallbacks();
clibxml2._xmlRegisterInputCallbacks(matchFunc, openFunc, readFunc, closeFunc);
clibxml2._xmlRegisterOutputCallbacks(matchFunc, openFunc, writeFunc, closeFunc);
// Set our external loader.
clibxml2._xmlSetExternalEntityLoader(externalLoader);
__Internal__.createStrPtr = function _createStrPtr(str, /*optional*/len) {
if (types.isNothing(len)) {
len = clibxml2.lengthBytesUTF8(str);
};
const ptr = clibxml2._malloc(len + 1);
if (ptr) {
clibxml2.stringToUTF8(str, ptr, len + 1);
};
return ptr;
};
__Internal__.initialized = true;
xml.registerParser(libxml2);
});
libxml2.ADD('parse', function(stream, /*optional*/options) {
const Promise = types.getPromise();
return Promise.try(function() {
if (!__Internal__.initialized) {
throw new types.ParseError("'libxml2' parser must be initialized first.");
};
let clibxml2 = null,
clibxml2Cleaned = false,
allocatedFunctions = null,
allocatedEntities = null,
sax = NULL,
saxOrg = NULL,
saxPtr = NULL,
userPtr = NULL,
userPtrOrg = NULL,
userPtrPtr = NULL,
urlPtr = NULL,
schemaParserCtxt = NULL,
schema = NULL,
validCtxt = NULL,
saxPlug = NULL,
pushParserCtxt = NULL;
let xsd = types.get(options, 'xsd', null);
let promise = null;
if (xsd) {
xsd = files.parseLocation(xsd);
promise = files.readFileAsync(xsd);
} else {
promise = Promise.resolve(null);
};
promise = promise.thenCreate(function libxml2ParserPromise(xsdContent, resolve, reject) {
// TODO: MemoryStream to replace strings
root.DD_ASSERT && root.DD_ASSERT(types._implements(stream, ioMixIns.TextInput) || types.isString(stream), "Invalid stream.");
clibxml2 = libxml2Loader.get();
const nodoc = types.get(options, 'nodoc', false),
discardEntities = types.get(options, 'discardEntities', false);
const entities = types.get(options, 'entities', null);
const PTR_LEN = clibxml2._xmlPtrLen();
const XML_INTERNAL_GENERAL_ENTITY = 1;
const XML_INTERNAL_PREDEFINED_ENTITY = 6;
let currentNode = null;
let callback = types.get(options, 'callback');
if (callback) {
const cbObj = types.get(options, 'callbackObj');
callback = doodad.Callback(cbObj, callback);
};
const doc = (nodoc ? null : new xml.Document());
const getStrFromXmlCharAr = function _getStrFromXmlCharAr(ptr, index, /*optional*/end) {
if (types.isNothing(end)) {
return clibxml2.Pointer_stringify(clibxml2.getValue(ptr + (PTR_LEN * index), '*'));
} else {
const startPtr = clibxml2.getValue(ptr + (PTR_LEN * index), '*');
const endPtr = clibxml2.getValue(ptr + (PTR_LEN * end), '*');
return clibxml2.Pointer_stringify(startPtr, endPtr - startPtr);
};
};
const SAX_HANDLERS = {
error: function error(ctxPtr, msgPtr, paramsPtr) {
const strPtr = clibxml2._xmlFormatGenericError(ctxPtr, msgPtr, paramsPtr);
const str = clibxml2.Pointer_stringify(strPtr);
if (str) {
tools.log(tools.LogLevels.Error, tools.trim(str, '\n'));
};
},
warning: function warning(ctxPtr, msgPtr, paramsPtr) {
const strPtr = clibxml2._xmlFormatGenericError(ctxPtr, msgPtr, paramsPtr);
const str = clibxml2.Pointer_stringify(strPtr);
if (str && (str.indexOf("Skipping import of schema") < 0)) {
tools.log(tools.LogLevels.Warning, tools.trim(str, '\n'));
};
},
//serror: function serror(userDataPtr, errorPtr) {
// types.DEBUGGER();
//},
//resolveEntity: function resolveEntity(ctxPtr, publicIdStrPtr, systemIdStrPtr) {
// types.DEBUGGER();
//},
getEntity: function getEntity(ctxPtr, namePtr) {
const name = clibxml2.Pointer_stringify(namePtr);
if (name === '__proto__') {
return NULL;
};
if (!allocatedEntities) {
allocatedEntities = tools.nullObject();
};
const resolved = types.get(allocatedEntities, name);
if (resolved) {
return allocatedEntities[name];
};
const entity = types.get(entities, name);
if (!entity) {
return NULL;
};
let newNamePtr = NULL,
strPtr = NULL;
try {
newNamePtr = __Internal__.createStrPtr(name);
if (!newNamePtr) {
throw new types.Error("Failed to allocate string buffer.");
};
strPtr = __Internal__.createStrPtr(entity);
if (!strPtr) {
throw new types.Error("Failed to allocate string buffer.");
};
const entityPtr = clibxml2._xmlNewEntity(NULL, newNamePtr, (discardEntities ? XML_INTERNAL_PREDEFINED_ENTITY : XML_INTERNAL_GENERAL_ENTITY), NULL, NULL, strPtr);
if (!entityPtr) {
throw new types.Error("Failed to allocate a new entity.");
};
allocatedEntities[name] = entityPtr;
return entityPtr;
} catch (ex) {
throw ex;
} finally {
if (newNamePtr) {
clibxml2._free(newNamePtr);
};
if (strPtr) {
clibxml2._free(strPtr);
};
};
},
externalSubset: function externalSubset(ctxPtr, namePtr, externalIDPtr, systemIDPtr) {
const node = new xml.DocumentType(clibxml2.Pointer_stringify(namePtr));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
doc.setDocumentType(node);
};
},
processingInstruction: function processingInstruction(ctxPtr, targetPtr, dataPtr) {
const node = new xml.ProcessingInstruction(clibxml2.Pointer_stringify(targetPtr), clibxml2.Pointer_stringify(dataPtr));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
doc.getInstructions().append(node);
};
},
comment: function comment(ctxPtr, valuePtr) {
const node = new xml.Comment(clibxml2.Pointer_stringify(valuePtr));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
currentNode.getChildren().append(node);
};
},
startElementNs: function startElementNs(ctxPtr, localnameStrPtr, prefixStrPtr, uriStrPtr, nb_namespaces, namespacesPtrStrPtr, nb_attributes, nb_defaulted, attributesPtrStrPtr) {
const node = new xml.Element(clibxml2.Pointer_stringify(localnameStrPtr), clibxml2.Pointer_stringify(prefixStrPtr), clibxml2.Pointer_stringify(uriStrPtr));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
currentNode.getChildren().append(node);
currentNode = node;
};
const attrs = (nodoc ? null : currentNode.getAttrs());
for (let i = 0; i < nb_attributes; i++) {
// localname/prefix/URI/value/end
const ptr = attributesPtrStrPtr + (PTR_LEN * 5 * i);
const node = new xml.Attribute(/*name*/getStrFromXmlCharAr(ptr, 0), /*value*/getStrFromXmlCharAr(ptr, 3, 4), /*prefix*/getStrFromXmlCharAr(ptr, 1), /*uri*/getStrFromXmlCharAr(ptr, 2));
//node.fileLine = line + 1;
//node.fileColumn = column + 1;
if (nodoc) {
callback(node);
} else {
attrs.append(node);
};
};
},
characters: function characters(ctxPtr, chPtr, len) {
const node = new xml.Text(clibxml2.Pointer_stringify(chPtr, len));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
currentNode.getChildren().append(node);
};
},
cdataBlock: function cdataBlock(ctxPtr, valuePtr, len) {
const node = new xml.CDATASection(clibxml2.Pointer_stringify(valuePtr, len));
//node.fileLine = parser.line + 1;
//node.fileColumn = parser.column + 1;
if (nodoc) {
callback(node);
} else {
currentNode.getChildren().append(node);
};
},
endElementNs: function endElementNs(ctxPtr, localnameStrPtr, prefixStrPtr, uriStrPtr) {
if (!nodoc) {
currentNode = currentNode.getParent();
};
},
endDocument: function endDocument(ctxPtr) {
if (nodoc) {
callback(null);
};
resolve(doc);
if (stream) {
stream.stopListening();
stream = null;
};
},
};
const allocFunction = function _allocFunction(name, sig) {
if (!allocatedFunctions) {
allocatedFunctions = tools.nullObject();
};
if (name in allocatedFunctions) {
return allocatedFunctions[name];
};
const fn = types.get(SAX_HANDLERS, name, null);
let ptr = NULL;
if (fn) {
ptr = clibxml2.addFunction(fn, sig);
if (!ptr) {
throw new types.Error("Failed to allocate function '~0~' for the SAXHandler.", [name]);
};
allocatedFunctions[name] = ptr;
};
return ptr;
};
sax = clibxml2._xmlCreateMySAXHandler(
allocFunction('internalSubset', 'viiii'),
allocFunction('isStandalone', 'ii'),
allocFunction('hasInternalSubset', 'ii'),
allocFunction('hasExternalSubset', 'ii'),
allocFunction('resolveEntity', 'iiii'),
allocFunction('getEntity', 'iii'),
allocFunction('entityDecl', 'viiiiii'),
allocFunction('notationDecl', 'viiii'),
allocFunction('attributeDecl', 'viiiiiii'),
allocFunction('elementDecl', 'viiii'),
allocFunction('unparsedEntityDecl', 'viiiii'),
allocFunction('setDocumentLocator', 'vii'),
allocFunction('startDocument', 'vi'),
allocFunction('endDocument', 'vi'),
allocFunction('startElement', 'viii'),
allocFunction('endElement', 'vii'),
allocFunction('reference', 'vii'),
allocFunction('characters', 'viii'),
allocFunction('ignorableWhitespace', 'viii'),
allocFunction('processingInstruction', 'viii'),
allocFunction('comment', 'vii'),
allocFunction('warning', 'viii'),
allocFunction('error', 'viii'),
allocFunction('getParameterEntity', 'iii'),
allocFunction('cdataBlock', 'viii'),
allocFunction('externalSubset', 'viiii'),
allocFunction('startElementNs', 'viiiiiiiii'),
allocFunction('endElementNs', 'viiii'),
allocFunction('serror', 'vii')
);
if (!sax) {
throw new types.Error("Failed to create SAXHandler.");
};
saxOrg = sax;
userPtr = clibxml2._malloc(4);
if (!userPtr) {
throw new types.Error("Failed to create user context.");
};
userPtrOrg = userPtr;
clibxml2.setValue(userPtr, 0, 'i32');
if (xsdContent) {
urlPtr = __Internal__.createStrPtr(xsd.toApiString());
if (!urlPtr) {
throw new types.Error("Failed to allocate URL string.");
};
schemaParserCtxt = clibxml2._xmlSchemaNewParserCtxt(urlPtr /*, userPtr WHEN POSSIBLE. FOR NOW, IT IS EQUAL TO schemaParserCtxt */);
if (!schemaParserCtxt) {
throw new types.Error("Failed to create schema parser.");
};
clibxml2._xmlSchemaSetParserErrors(schemaParserCtxt, allocFunction('error', 'viii'), allocFunction('warning', 'viii'), NULL);
schema = clibxml2._xmlSchemaParse(schemaParserCtxt);
if (!schema) {
throw new types.Error("Failed to parse schema.");
};
clibxml2._free(urlPtr); // free memory
urlPtr = NULL;
__Internal__.unregisterBaseDirectories(schemaParserCtxt); // Use userPtrOrg WHEN POSSIBLE
clibxml2._xmlSchemaFreeParserCtxt(schemaParserCtxt);
schemaParserCtxt = NULL;
validCtxt = clibxml2._xmlSchemaNewValidCtxt(schema);
if (!validCtxt) {
throw new types.Error("Failed to create schema validator.");
};
//clibxml2._xmlSchemaSetValidErrors(validCtxt, allocFunction('error', 'viii'), allocFunction('warning', 'viii'), userPtr);
saxPtr = clibxml2._malloc(PTR_LEN);
if (!saxPtr) {
throw new types.Error("Failed to create SAX pointer.");
};
clibxml2.setValue(saxPtr, sax, '*');
userPtrPtr = clibxml2._malloc(PTR_LEN);
if (!userPtrPtr) {
throw new types.Error("Failed to create user context pointer.");
};
clibxml2.setValue(userPtrPtr, userPtr, '*');
saxPlug = clibxml2._xmlSchemaSAXPlug(validCtxt, saxPtr, userPtrPtr);
if (!saxPlug) {
throw new types.Error("Failed to plug schema with SAX.");
};
sax = clibxml2.getValue(saxPtr, '*');
userPtr = clibxml2.getValue(userPtrPtr, '*');
};
if (!nodoc && !discardEntities) {
tools.forEach(entities, function(value, name) {
const node = new xml.Entity(name, value);
if (nodoc) {
callback(node);
} else {
doc.getEntities().append(node);
};
});
};
currentNode = doc;
pushParserCtxt = clibxml2._xmlCreatePushParserCtxt(sax, userPtr, NULL, 0, NULL);
if (!pushParserCtxt) {
throw new types.Error("Failed to create push parser.");
};
if (types.isString(stream)) {
let valuePtr = NULL;
try {
const len = clibxml2.lengthBytesUTF8(stream);
valuePtr = __Internal__.createStrPtr(stream, len);
if (!valuePtr) {
throw new types.Error("Failed to allocate string buffer.");
};
stream = null;
const res = clibxml2._xmlParseChunk(pushParserCtxt, valuePtr, len, 1);
if (res) {
throw new types.Error("Failed to parse the XML document.");
};
const isValid = clibxml2._xmlSchemaIsValid(validCtxt);
if (isValid <= 0) {
throw new types.Error("The XML document is invalid.");
};
} catch(ex) {
reject(ex);
} finally {
if (valuePtr) {
clibxml2._free(valuePtr);
valuePtr = NULL;
};
};
} else {
stream.onError.attachOnce(this, function(ev) {
ev.preventDefault();
reject(ev.error);
});
stream.onReady.attach(this, function(ev) {
ev.preventDefault();
let valuePtr = NULL;
try {
if (ev.data.raw === io.EOF) {
const res = clibxml2._xmlParseChunk(pushParserCtxt, NULL, 0, 1);
if (res) {
throw new types.Error("Failed to close document.");
};
} else {
const value = ev.data.valueOf();
if (types.isString(value)) {
const len = clibxml2.lengthBytesUTF8(value);
valuePtr = __Internal__.createStrPtr(value, len);
if (!valuePtr) {
throw new types.Error("Failed to allocate string buffer.");
};
const res = clibxml2._xmlParseChunk(pushParserCtxt, valuePtr, len, 0);
if (res) {
throw new types.Error("Failed to parse chunk.");
};
} else {
// TODO: Non UTF-8
valuePtr = clibxml2.allocate(value, 'i8', clibxml2.ALLOC_NORMAL);
if (!valuePtr) {
throw new types.Error("Failed to allocate value buffer.");
};
const res = clibxml2._xmlParseChunk(pushParserCtxt, valuePtr, value.length, 0);
if (res) {
throw new types.Error("Failed to parse chunk.");
};
};
};
const isValid = clibxml2._xmlSchemaIsValid(validCtxt);
if (isValid <= 0) {
throw new types.Error("The XML document is invalid.");
};
} catch(ex) {
throw ex;
} finally {
if (valuePtr) {
clibxml2._free(valuePtr);
valuePtr = NULL;
};
};
});
stream.listen();
};
})
.nodeify(function(err, result) {
if (pushParserCtxt) {
clibxml2._xmlFreeParserCtxt(pushParserCtxt);
pushParserCtxt = NULL;
};
if (saxPlug) {
clibxml2._xmlSchemaSAXUnplug(saxPlug);
saxPlug = NULL;
//sax = NULL; <PRB> Code commented because "sax" returned from "xmlSchemaSAXPlug" is not freed
userPtr = NULL; // OK
};
if (validCtxt) {
clibxml2._xmlSchemaFreeValidCtxt(validCtxt);
validCtxt = NULL;
};
if (schema) {
clibxml2._xmlSchemaFree(schema);
schema = NULL;
};
if (schemaParserCtxt) {
try {
__Internal__.unregisterBaseDirectories(schemaParserCtxt); // Use userPtrOrg WHEN POSSIBLE
} catch(ex) {
// Ignore
};
clibxml2._xmlSchemaFreeParserCtxt(schemaParserCtxt);
schemaParserCtxt = NULL;
};
if (urlPtr) {
clibxml2._free(urlPtr);
urlPtr = NULL;
};
if (allocatedFunctions) {
tools.forEach(allocatedFunctions, function(ptr, name) {
clibxml2.removeFunction(ptr);
});
allocatedFunctions = null;
};
if (allocatedEntities) {
tools.forEach(allocatedEntities, function(ptr) {
clibxml2._xmlFreeEntity(ptr);
});
allocatedEntities = null;
};
if (saxPtr) {
clibxml2._free(saxPtr);
saxPtr = NULL;
};
if (sax && (sax !== saxOrg)) {
clibxml2._xmlFreeEx(sax);
sax = NULL;
};
if (saxOrg) {
clibxml2._xmlFreeMySAXHandler(saxOrg);
saxOrg = NULL;
};
if (userPtrPtr) {
clibxml2._free(userPtrPtr);
userPtrPtr = NULL;
};
if (userPtr && (userPtr !== userPtrOrg)) {
clibxml2._xmlFreeEx(userPtr);
userPtr = NULL;
};
if (userPtrOrg) {
clibxml2._free(userPtrOrg);
userPtrOrg = NULL;
};
clibxml2 = null;
clibxml2Cleaned = true;
if (err) {
throw err;
} else {
return result;
};
})
.catch(function(err) {
if (!clibxml2Cleaned && !types._instanceof(err, libxml2.AbortError)) {
// Lixml2 is unstable because its cleanup has failed, force abort.
throw new libxml2.AbortError(err);
};
throw err;
});
return promise;
});
});
libxml2.ADD('isAvailable', function isAvailable() {
return __Internal__.initialized;
});
libxml2.ADD('hasFeatures', function hasFeatures(features) {
if (!__Internal__.initialized) {
return false;
};
// <PRB> libxml2 schema files loader is not Asynchronous so we can't use schemas on the client.
// <PRB> libxml2's schema validation is not ready for procuction use with Node because of it's synchronous design.
const current = {
schemas: (root.serverSide && root.getOptions().debug),
};
return tools.every(features, function(wanted, name) {
return !wanted || types.get(current, name, false);
});
});
//===================================
// Init
//===================================
return function init(/*optional*/options) {
const clibxml2 = libxml2Loader.get();
if (clibxml2) {
libxml2.init();
};
};
},
};
return modules;
};