@@ -108,10 +108,27 @@ function Namespace(name, options) {
108108 * @private
109109 */
110110 this . _nestedArray = null ;
111+
112+ /**
113+ * Cache lookup calls for any objects contains anywhere under this namespace.
114+ * This drastically speeds up resolve for large cross-linked protos where the same
115+ * types are looked up repeatedly.
116+ * @type {Object.<string,ReflectionObject|null> }
117+ * @private
118+ */
119+ this . _lookupCache = { } ;
111120}
112121
113122function clearCache ( namespace ) {
114123 namespace . _nestedArray = null ;
124+ namespace . _lookupCache = { } ;
125+
126+ // Also clear parent caches, since they include nested lookups.
127+ var parent = namespace . parent ;
128+ while ( parent ) {
129+ parent . _lookupCache = { } ;
130+ parent = parent . parent ;
131+ }
115132 return namespace ;
116133}
117134
@@ -341,7 +358,6 @@ Namespace.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursi
341358 * @returns {ReflectionObject|null } Looked up object or `null` if none could be found
342359 */
343360Namespace . prototype . lookup = function lookup ( path , filterTypes , parentAlreadyChecked ) {
344-
345361 /* istanbul ignore next */
346362 if ( typeof filterTypes === "boolean" ) {
347363 parentAlreadyChecked = filterTypes ;
@@ -360,25 +376,48 @@ Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChe
360376 if ( path [ 0 ] === "" )
361377 return this . root . lookup ( path . slice ( 1 ) , filterTypes ) ;
362378
363- // Test if the first part matches any nested object, and if so, traverse if path contains more
364- var found = this . get ( path [ 0 ] ) ;
365- if ( found ) {
366- if ( path . length === 1 ) {
367- if ( ! filterTypes || filterTypes . indexOf ( found . constructor ) > - 1 )
368- return found ;
369- } else if ( found instanceof Namespace && ( found = found . lookup ( path . slice ( 1 ) , filterTypes , true ) ) )
370- return found ;
371-
372- // Otherwise try each nested namespace
373- } else
374- for ( var i = 0 ; i < this . nestedArray . length ; ++ i )
375- if ( this . _nestedArray [ i ] instanceof Namespace && ( found = this . _nestedArray [ i ] . lookup ( path , filterTypes , true ) ) )
376- return found ;
379+ var found = this . _lookupImpl ( path ) ;
380+ if ( found && ( ! filterTypes || filterTypes . indexOf ( found . constructor ) > - 1 ) ) {
381+ return found ;
382+ }
377383
378384 // If there hasn't been a match, try again at the parent
379385 if ( this . parent === null || parentAlreadyChecked )
380386 return null ;
381387 return this . parent . lookup ( path , filterTypes ) ;
388+ }
389+
390+ /**
391+ * Internal helper for lookup that handles searching just at this namespace and below along with caching.
392+ * @param {string[] } path Path to look up
393+ * @returns {ReflectionObject|null } Looked up object or `null` if none could be found
394+ * @private
395+ */
396+ Namespace . prototype . _lookupImpl = function lookup ( path ) {
397+ var flatPath = path . join ( "." ) ;
398+ if ( this . _lookupCache . hasOwnProperty ( flatPath ) ) {
399+ return this . _lookupCache [ flatPath ] ;
400+ } else {
401+ // Test if the first part matches any nested object, and if so, traverse if path contains more
402+ var found = this . get ( path [ 0 ] ) ;
403+ var exact = null ;
404+ if ( found ) {
405+ if ( path . length === 1 ) {
406+ exact = found ;
407+ } else if ( found instanceof Namespace && ( found = found . _lookupImpl ( path . slice ( 1 ) ) ) )
408+ exact = found ;
409+
410+ // Otherwise try each nested namespace
411+ } else {
412+ for ( var i = 0 ; i < this . nestedArray . length ; ++ i )
413+ if ( this . _nestedArray [ i ] instanceof Namespace && ( found = this . _nestedArray [ i ] . _lookupImpl ( path ) ) )
414+ exact = found ;
415+ }
416+
417+ // Set this even when null, so that when we walk up the tree we can quickly bail on repeated checks back down.
418+ this . _lookupCache [ flatPath ] = exact ;
419+ return exact ;
420+ }
382421} ;
383422
384423/**
0 commit comments