Skip to content

Commit

Permalink
Merge pull request #195 from paztis/master
Browse files Browse the repository at this point in the history
Performances problems fixes on big files with a lot of references on same big object
  • Loading branch information
P0lip authored Dec 9, 2020
2 parents d3bc198 + 991e1a0 commit 13b0092
Showing 1 changed file with 73 additions and 36 deletions.
109 changes: 73 additions & 36 deletions lib/dereference.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = dereference;
*/
function dereference (parser, options) {
// console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path);
let dereferenced = crawl(parser.schema, parser.$refs._root$Ref.path, "#", [], parser.$refs, options);
let dereferenced = crawl(parser.schema, parser.$refs._root$Ref.path, "#", [], [], {}, parser.$refs, options);
parser.$refs.circular = dereferenced.circular;
parser.schema = dereferenced.value;
}
Expand All @@ -28,60 +28,65 @@ function dereference (parser, options) {
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
* @param {string} pathFromRoot - The path of `obj` from the schema root
* @param {object[]} parents - An array of the parent objects that have already been dereferenced
* @param {object[]} processedObjects - An array of all the objects that have already been processed
* @param {object} dereferencedCache - An map of all the dereferenced objects
* @param {$Refs} $refs
* @param {$RefParserOptions} options
* @returns {{value: object, circular: boolean}}
*/
function crawl (obj, path, pathFromRoot, parents, $refs, options) {
function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options) {
let dereferenced;
let result = {
value: obj,
circular: false
};

if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
parents.push(obj);
if (options.dereference.circular === "ignore" || processedObjects.indexOf(obj) === -1) {
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
parents.push(obj);
processedObjects.push(obj);

if ($Ref.isAllowed$Ref(obj, options)) {
dereferenced = dereference$Ref(obj, path, pathFromRoot, parents, $refs, options);
result.circular = dereferenced.circular;
result.value = dereferenced.value;
}
else {
for (let key of Object.keys(obj)) {
let keyPath = Pointer.join(path, key);
let keyPathFromRoot = Pointer.join(pathFromRoot, key);
let value = obj[key];
let circular = false;

if ($Ref.isAllowed$Ref(value, options)) {
dereferenced = dereference$Ref(value, keyPath, keyPathFromRoot, parents, $refs, options);
circular = dereferenced.circular;
// Avoid pointless mutations; breaks frozen objects to no profit
if (obj[key] !== dereferenced.value) {
obj[key] = dereferenced.value;
}
}
else {
if (parents.indexOf(value) === -1) {
dereferenced = crawl(value, keyPath, keyPathFromRoot, parents, $refs, options);
if ($Ref.isAllowed$Ref(obj, options)) {
dereferenced = dereference$Ref(obj, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
result.circular = dereferenced.circular;
result.value = dereferenced.value;
}
else {
for (let key of Object.keys(obj)) {
let keyPath = Pointer.join(path, key);
let keyPathFromRoot = Pointer.join(pathFromRoot, key);
let value = obj[key];
let circular = false;

if ($Ref.isAllowed$Ref(value, options)) {
dereferenced = dereference$Ref(value, keyPath, keyPathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
circular = dereferenced.circular;
// Avoid pointless mutations; breaks frozen objects to no profit
if (obj[key] !== dereferenced.value) {
obj[key] = dereferenced.value;
}
}
else {
circular = foundCircularReference(keyPath, $refs, options);
if (parents.indexOf(value) === -1) {
dereferenced = crawl(value, keyPath, keyPathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
circular = dereferenced.circular;
// Avoid pointless mutations; breaks frozen objects to no profit
if (obj[key] !== dereferenced.value) {
obj[key] = dereferenced.value;
}
}
else {
circular = foundCircularReference(keyPath, $refs, options);
}
}
}

// Set the "isCircular" flag if this or any other property is circular
result.circular = result.circular || circular;
// Set the "isCircular" flag if this or any other property is circular
result.circular = result.circular || circular;
}
}
}

parents.pop();
parents.pop();
}
}

return result;
Expand All @@ -94,14 +99,38 @@ function crawl (obj, path, pathFromRoot, parents, $refs, options) {
* @param {string} path - The full path of `$ref`, possibly with a JSON Pointer in the hash
* @param {string} pathFromRoot - The path of `$ref` from the schema root
* @param {object[]} parents - An array of the parent objects that have already been dereferenced
* @param {object[]} processedObjects - An array of all the objects that have already been dereferenced
* @param {object} dereferencedCache - An map of all the dereferenced objects
* @param {$Refs} $refs
* @param {$RefParserOptions} options
* @returns {{value: object, circular: boolean}}
*/
function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
function dereference$Ref ($ref, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options) {
// console.log('Dereferencing $ref pointer "%s" at %s', $ref.$ref, path);

let $refPath = url.resolve(path, $ref.$ref);

if (dereferencedCache[$refPath]) {
const cache = dereferencedCache[$refPath];

const refKeys = Object.keys($ref);
if (refKeys.length > 1) {
const extraKeys = {};
for (let key of refKeys) {
if (key !== "$ref" && !(key in cache.value)) {
extraKeys[key] = $ref[key];
}
}
return {
circular: cache.circular,
value: Object.assign({}, cache.value, extraKeys),
};
}

return cache;
}


let pointer = $refs._resolve($refPath, path, options);

if (pointer === null) {
Expand All @@ -122,7 +151,7 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
// Crawl the dereferenced value (unless it's circular)
if (!circular) {
// Determine if the dereferenced value is circular
let dereferenced = crawl(dereferencedValue, pointer.path, pathFromRoot, parents, $refs, options);
let dereferenced = crawl(dereferencedValue, pointer.path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
circular = dereferenced.circular;
dereferencedValue = dereferenced.value;
}
Expand All @@ -138,10 +167,18 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
dereferencedValue.$ref = pathFromRoot;
}

return {

const dereferencedObject = {
circular,
value: dereferencedValue
};

// only cache if no extra properties than $ref
if (Object.keys($ref).length === 1) {
dereferencedCache[$refPath] = dereferencedObject;
}

return dereferencedObject;
}

/**
Expand Down

0 comments on commit 13b0092

Please sign in to comment.