@@ -108,10 +108,33 @@ 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 = { } ;
120+
121+ /**
122+ * Whether or not objects contained in this namespace need feature resolution.
123+ * @type {boolean }
124+ * @protected
125+ */
126+ this . _needsRecursiveFeatureResolution = true ;
111127}
112128
113129function clearCache ( namespace ) {
114130 namespace . _nestedArray = null ;
131+ namespace . _lookupCache = { } ;
132+
133+ // Also clear parent caches, since they include nested lookups.
134+ var parent = namespace ;
135+ while ( parent = parent . parent ) {
136+ parent . _lookupCache = { } ;
137+ }
115138 return namespace ;
116139}
117140
@@ -249,6 +272,14 @@ Namespace.prototype.add = function add(object) {
249272 }
250273 }
251274
275+ this . _needsRecursiveFeatureResolution = true ;
276+
277+ // Also clear parent caches, since they need to recurse down.
278+ var parent = this ;
279+ while ( parent = parent . parent ) {
280+ parent . _needsRecursiveFeatureResolution = true ;
281+ }
282+
252283 object . onAdd ( this ) ;
253284 return clearCache ( this ) ;
254285} ;
@@ -324,6 +355,9 @@ Namespace.prototype.resolveAll = function resolveAll() {
324355 * @override
325356 */
326357Namespace . prototype . _resolveFeaturesRecursive = function _resolveFeaturesRecursive ( edition ) {
358+ if ( ! this . _needsRecursiveFeatureResolution ) return this ;
359+ this . _needsRecursiveFeatureResolution = false ;
360+
327361 edition = this . _edition || edition ;
328362
329363 ReflectionObject . prototype . _resolveFeaturesRecursive . call ( this , edition ) ;
@@ -341,7 +375,6 @@ Namespace.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursi
341375 * @returns {ReflectionObject|null } Looked up object or `null` if none could be found
342376 */
343377Namespace . prototype . lookup = function lookup ( path , filterTypes , parentAlreadyChecked ) {
344-
345378 /* istanbul ignore next */
346379 if ( typeof filterTypes === "boolean" ) {
347380 parentAlreadyChecked = filterTypes ;
@@ -360,25 +393,48 @@ Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChe
360393 if ( path [ 0 ] === "" )
361394 return this . root . lookup ( path . slice ( 1 ) , filterTypes ) ;
362395
396+ var found = this . _lookupImpl ( path ) ;
397+ if ( found && ( ! filterTypes || filterTypes . indexOf ( found . constructor ) > - 1 ) ) {
398+ return found ;
399+ }
400+
401+ // If there hasn't been a match, try again at the parent
402+ if ( this . parent === null || parentAlreadyChecked )
403+ return null ;
404+ return this . parent . lookup ( path , filterTypes ) ;
405+ } ;
406+
407+ /**
408+ * Internal helper for lookup that handles searching just at this namespace and below along with caching.
409+ * @param {string[] } path Path to look up
410+ * @returns {ReflectionObject|null } Looked up object or `null` if none could be found
411+ * @private
412+ */
413+ Namespace . prototype . _lookupImpl = function lookup ( path ) {
414+ var flatPath = path . join ( "." ) ;
415+ if ( Object . prototype . hasOwnProperty . call ( this . _lookupCache , flatPath ) ) {
416+ return this . _lookupCache [ flatPath ] ;
417+ }
418+
363419 // Test if the first part matches any nested object, and if so, traverse if path contains more
364420 var found = this . get ( path [ 0 ] ) ;
421+ var exact = null ;
365422 if ( found ) {
366423 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 ;
424+ exact = found ;
425+ } else if ( found instanceof Namespace && ( found = found . _lookupImpl ( path . slice ( 1 ) ) ) )
426+ exact = found ;
371427
372428 // Otherwise try each nested namespace
373- } else
429+ } else {
374430 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 ;
431+ if ( this . _nestedArray [ i ] instanceof Namespace && ( found = this . _nestedArray [ i ] . _lookupImpl ( path ) ) )
432+ exact = found ;
433+ }
377434
378- // If there hasn't been a match, try again at the parent
379- if ( this . parent === null || parentAlreadyChecked )
380- return null ;
381- return this . parent . lookup ( path , filterTypes ) ;
435+ // Set this even when null, so that when we walk up the tree we can quickly bail on repeated checks back down.
436+ this . _lookupCache [ flatPath ] = exact ;
437+ return exact ;
382438} ;
383439
384440/**
0 commit comments