Chromium Code Reviews| Index: lib/src/compiler/code_generator.dart |
| diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
| index ba65adf354cb69cffa88c9b4bcc8fb43bc2759e8..bbe4e74655aeb14bb5730a87b8760c4abd8a602d 100644 |
| --- a/lib/src/compiler/code_generator.dart |
| +++ b/lib/src/compiler/code_generator.dart |
| @@ -66,6 +66,9 @@ class CodeGenerator extends GeneralizingAstVisitor |
| /// The list of output module items, in the order they need to be emitted in. |
| final _moduleItems = <JS.ModuleItem>[]; |
| + /// Table of named and possibly hoisted types. |
| + final _typeTable = new _TypeTable(); |
| + |
| /// The global extension type table. |
| final ExtensionTypeSet _extensionTypes; |
| @@ -252,6 +255,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
| // Declare imports |
| _finishImports(items); |
| + // Discharge the type table cache variables and |
| + // hoisted definitions. |
| + items.addAll(_typeTable.discharge()); |
| + |
| // Add the module's code (produced by visiting compilation units, above) |
| _copyAndFlattenBlocks(items, _moduleItems); |
| @@ -568,7 +575,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
| JS.Expression body = annotate( |
| js.call('dart.typedef(#, () => #)', [ |
| js.string(element.name, "'"), |
| - _emitType(element.type, lowerTypedef: true) |
| + _emitType(element.type, nameType: false, lowerTypedef: true) |
| ]), |
| node, |
| element); |
| @@ -840,8 +847,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
| JS.Statement _defineClassTypeArguments(TypeDefiningElement element, |
| List<TypeParameterElement> formals, JS.Statement body) { |
| assert(formals.isNotEmpty); |
| - var genericCall = js.call('dart.generic((#) => { #; return #; })', |
| - [_emitTypeFormals(formals), body, element.name]); |
| + var genericCall = js.call('dart.generic((#) => { #; #; return #; })', [ |
| + _emitTypeFormals(formals), |
| + _typeTable.discharge(formals), |
| + body, |
| + element.name |
| + ]); |
| if (element.library.isDartAsync && |
| (element.name == "Future" || element.name == "_Future")) { |
| genericCall = js.call('dart.flattenFutures(#)', [genericCall]); |
| @@ -894,10 +905,13 @@ class CodeGenerator extends GeneralizingAstVisitor |
| supertype = fillDynamicTypeArgs(supertype.element.type); |
| _hasDeferredSupertype.add(element); |
| } |
| - heritage = _emitType(supertype); |
| + // We could choose to name the superclasses, but it's |
| + // not clear that there's much benefit |
| + heritage = _emitType(supertype, nameType: false); |
| if (type.mixins.isNotEmpty) { |
| - var mixins = type.mixins.map(_emitType).toList(); |
| + var mixins = |
| + type.mixins.map((t) => _emitType(t, nameType: false)).toList(); |
| mixins.insert(0, heritage); |
| heritage = js.call('dart.mixin(#)', [mixins]); |
| } |
| @@ -1159,7 +1173,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
| 'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); |
| } else if (_hasDeferredSupertype.contains(classElem)) { |
| var newBaseClass = _emitType(classElem.type.superclass, |
| - subClass: classElem, className: className); |
| + nameType: false, subClass: classElem, className: className); |
| body.add( |
| js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
| } |
| @@ -1259,9 +1273,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
| } |
| var memberName = _elementMemberName(element, |
| useExtension: _extensionTypes.isNativeClass(classElem)); |
| - var parts = _emitFunctionTypeParts(element.type); |
| - var property = |
| - new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| + // TODO(leafp): Settle on a policy for this. Naming these makes the |
| + // signature more compact, but it generates a lot of noise at the |
| + // top level, and it's not clear that there's much benefit. |
| + var type = |
| + _emitFunctionType(element.type, nameType: false, definite: true); |
| + var property = new JS.Property(memberName, type); |
| if (node.isStatic) { |
| tStatics.add(property); |
| sNames.add(memberName); |
| @@ -1275,10 +1292,11 @@ class CodeGenerator extends GeneralizingAstVisitor |
| for (ConstructorDeclaration node in ctors) { |
| var memberName = _constructorName(node.element); |
| var element = node.element; |
| - var parts = _emitFunctionTypeParts(element.type, |
| - parameters: node.parameters.parameters); |
| - var property = |
| - new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| + var type = _emitFunctionType(element.type, |
| + parameters: node.parameters.parameters, |
| + nameType: false, |
| + definite: true); |
| + var property = new JS.Property(memberName, type); |
| tCtors.add(property); |
| } |
| @@ -1918,25 +1936,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
| JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
| {topLevel: false}) { |
| - var name = type.name; |
| var lazy = topLevel && !_typeIsLoaded(type); |
| - |
| - if (type is FunctionType && (name == '' || name == null)) { |
| - if (type.typeFormals.isEmpty && |
| - type.returnType.isDynamic && |
| - type.optionalParameterTypes.isEmpty && |
| - type.namedParameterTypes.isEmpty && |
| - type.normalParameterTypes.every((t) => t.isDynamic)) { |
| - return js.call('dart.fn(#)', [fn]); |
| - } |
| - |
| - var parts = _emitFunctionTypeParts(type); |
| - if (lazy) { |
| - return js.call( |
| - 'dart.lazyFn(#, () => #)', [fn, new JS.ArrayInitializer(parts)]); |
| - } else { |
| - return js.call('dart.fn(#, #)', [fn, parts]); |
| - } |
| + var typeRep = _emitFunctionType(type, definite: true); |
| + if (lazy) { |
| + return js.call('dart.lazyFn(#, () => #)', [fn, typeRep]); |
| + } else { |
| + return js.call('dart.fn(#, #)', [fn, typeRep]); |
| } |
| throw 'Function has non function type: $type'; |
| } |
| @@ -2012,21 +2017,21 @@ class CodeGenerator extends GeneralizingAstVisitor |
| FormalParameterList parameters, FunctionBody body) { |
| FunctionType type = element.type; |
| - // sync*, async, async* |
| - if (element.isAsynchronous || element.isGenerator) { |
| - return new JS.Fun( |
| - visitFormalParameterList(parameters, destructure: false), |
| - new JS.Block([ |
| - _emitGeneratorFunctionBody(element, parameters, body).toReturn() |
| - ]), |
| - typeParams: _emitTypeFormals(type.typeFormals), |
| - returnType: emitTypeRef(type.returnType)); |
| + // normal function (sync), vs (sync*, async, async*) |
| + var stdFn = !(element.isAsynchronous || element.isGenerator); |
| + var formals = visitFormalParameterList(parameters, destructure: stdFn); |
| + var code = (stdFn) |
| + ? _visit(body) |
| + : new JS.Block( |
| + [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); |
| + var typeFormals = _emitTypeFormals(type.typeFormals); |
| + var returnType = emitTypeRef(type.returnType); |
| + if (type.typeFormals.isNotEmpty) { |
| + code = new JS.Block( |
| + [new JS.Block(_typeTable.discharge(type.typeFormals)), code]); |
| } |
| - |
| - // normal function (sync) |
| - return new JS.Fun(visitFormalParameterList(parameters), _visit(body), |
| - typeParams: _emitTypeFormals(type.typeFormals), |
| - returnType: emitTypeRef(type.returnType)); |
| + return new JS.Fun(formals, code, |
| + typeParams: typeFormals, returnType: returnType); |
| } |
| JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
| @@ -2249,13 +2254,15 @@ class CodeGenerator extends GeneralizingAstVisitor |
| ? p.metadata |
| : (p as DefaultFormalParameter).parameter.metadata; |
| - JS.ArrayInitializer _emitTypeNames(List<DartType> types, |
| - [List<FormalParameter> parameters]) { |
| + JS.ArrayInitializer _emitTypeNames( |
| + List<DartType> types, List<FormalParameter> parameters, |
| + {bool nameType: true, bool hoistType: true}) { |
| var result = <JS.Expression>[]; |
| for (int i = 0; i < types.length; ++i) { |
| var metadata = |
| parameters != null ? _parameterMetadata(parameters[i]) : []; |
| - var typeName = _emitType(types[i]); |
| + var typeName = |
| + _emitType(types[i], nameType: nameType, hoistType: hoistType); |
| var value = typeName; |
| if (options.emitMetadata && metadata.isNotEmpty) { |
| metadata = metadata.map(_instantiateAnnotation).toList(); |
| @@ -2278,13 +2285,37 @@ class CodeGenerator extends GeneralizingAstVisitor |
| /// Emit the pieces of a function type, as an array of return type, |
| /// regular args, and optional/named args. |
| + JS.Expression _emitFunctionType(FunctionType type, |
| + {List<FormalParameter> parameters, |
| + bool lowerTypedef: false, |
| + bool nameType: true, |
| + bool hoistType: true, |
|
Jennifer Messerly
2016/05/24 21:56:39
do we ever set this to false? I wasn't able to fin
Leaf
2016/05/25 00:56:13
It was here so that we can play with different str
|
| + definite: false}) { |
| + var parts = _emitFunctionTypeParts(type, |
| + parameters: parameters, |
| + lowerTypedef: lowerTypedef, |
| + nameType: nameType, |
| + hoistType: hoistType); |
| + var helper = (definite) ? 'definiteFunctionType' : 'functionType'; |
| + var fullType = js.call('dart.${helper}(#)', [parts]); |
| + if (!nameType) return fullType; |
| + return _typeTable.nameType(type, fullType, hoistType, definite: definite); |
| + } |
| + |
| + /// Emit the pieces of a function type, as an array of return type, |
| + /// regular args, and optional/named args. |
| List<JS.Expression> _emitFunctionTypeParts(FunctionType type, |
| - {List<FormalParameter> parameters, bool lowerTypedef: false}) { |
| + {List<FormalParameter> parameters, |
| + bool lowerTypedef: false, |
| + bool nameType: true, |
| + bool hoistType: true}) { |
| var parameterTypes = type.normalParameterTypes; |
| var optionalTypes = type.optionalParameterTypes; |
| var namedTypes = type.namedParameterTypes; |
| - var rt = _emitType(type.returnType); |
| - var ra = _emitTypeNames(parameterTypes, parameters); |
| + var rt = |
| + _emitType(type.returnType, nameType: nameType, hoistType: hoistType); |
| + var ra = _emitTypeNames(parameterTypes, parameters, |
| + nameType: nameType, hoistType: hoistType); |
| List<JS.Expression> typeParts; |
| if (namedTypes.isNotEmpty) { |
| @@ -2295,7 +2326,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
| } else if (optionalTypes.isNotEmpty) { |
| assert(namedTypes.isEmpty); |
| var oa = _emitTypeNames( |
| - optionalTypes, parameters?.sublist(parameterTypes.length)); |
| + optionalTypes, parameters?.sublist(parameterTypes.length), |
| + nameType: nameType, hoistType: hoistType); |
| typeParts = [rt, ra, oa]; |
| } else { |
| typeParts = [rt, ra]; |
| @@ -2307,7 +2339,17 @@ class CodeGenerator extends GeneralizingAstVisitor |
| // function types (as callable functions). See discussion at: |
| // https://github.com/dart-lang/dev_compiler/issues/526 |
| var tf = _emitTypeFormals(typeFormals); |
| - typeParts = [new JS.ArrowFun(tf, new JS.ArrayInitializer(typeParts))]; |
| + var names = _typeTable.discharge(typeFormals); |
| + var parts = new JS.ArrayInitializer(typeParts); |
| + if (names.isEmpty) { |
| + typeParts = [ |
| + js.call('(#) => #', [tf, parts]) |
| + ]; |
| + } else { |
| + typeParts = [ |
| + js.call('(#) => {#; return #;}', [tf, names, parts]) |
| + ]; |
| + } |
| } |
| return typeParts; |
| } |
| @@ -2325,6 +2367,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
| JS.Expression _emitType(DartType type, |
| {bool lowerTypedef: false, |
| bool lowerGeneric: false, |
| + bool nameType: true, |
| + bool hoistType: true, |
| ClassElement subClass, |
| JS.Expression className}) { |
| // The void and dynamic types are not defined in core. |
| @@ -2347,9 +2391,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
| // TODO(jmesserly): should we change how typedefs work? They currently |
| // go through use similar logic as generic classes. This makes them |
| // different from universal function types. |
| - var ft = type as FunctionType; |
| - var parts = _emitFunctionTypeParts(ft, lowerTypedef: lowerTypedef); |
| - return js.call('dart.functionType(#)', [parts]); |
| + return _emitFunctionType(type as FunctionType, |
| + lowerTypedef: lowerTypedef, nameType: nameType, hoistType: hoistType); |
| } |
| if (type is TypeParameterType) { |
| @@ -2365,14 +2408,20 @@ class CodeGenerator extends GeneralizingAstVisitor |
| var args = type.typeArguments; |
| Iterable jsArgs = null; |
| if (args.any((a) => !a.isDynamic)) { |
| - jsArgs = args |
| - .map((x) => _emitType(x, subClass: subClass, className: className)); |
| + jsArgs = args.map((x) => _emitType(x, |
| + nameType: nameType, |
| + hoistType: hoistType, |
| + subClass: subClass, |
| + className: className)); |
| } else if (lowerGeneric) { |
| jsArgs = []; |
| } |
| if (jsArgs != null) { |
| var genericName = _emitTopLevelName(element, suffix: '\$'); |
| - return js.call('#(#)', [genericName, jsArgs]); |
| + var typeRep = js.call('#(#)', [genericName, jsArgs]); |
| + return nameType |
| + ? _typeTable.nameType(type, typeRep, hoistType) |
| + : typeRep; |
| } |
| } |
| @@ -4565,3 +4614,210 @@ LibraryElement _getLibrary(AnalysisContext c, String uri) => |
| bool _isDartRuntime(LibraryElement l) => |
| l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
| + |
| +Set<TypeParameterElement> _freeTypeParameters(DartType t) { |
| + var result = new HashSet<TypeParameterElement>(); |
| + void find(DartType t) { |
| + if (t is TypeParameterType) { |
| + result.add(t.element); |
| + } else if (t is FunctionType) { |
| + find(t.returnType); |
| + t.parameters.forEach((p) => find(p.type)); |
| + t.typeFormals.forEach((p) => find(p.bound)); |
| + t.typeFormals.forEach(result.remove); |
| + } else if (t is InterfaceType) { |
| + t.typeArguments.forEach(find); |
| + } |
| + } |
| + find(t); |
| + return result; |
| +} |
| + |
| +/// _CacheTable tracks cache variables for variables that |
| +/// are emitted in place with a hoisted variable for a cache. |
|
Jennifer Messerly
2016/05/24 21:56:39
Consider: including an example here of what it loo
Leaf
2016/05/25 00:56:13
Done below.
|
| +class _CacheTable { |
|
Jennifer Messerly
2016/05/24 21:56:39
consider: putting these new classes into their own
Leaf
2016/05/25 00:56:13
Done.
|
| + /// Mapping from types to their canonical names. |
| + final _names = new Map<DartType, JS.TemporaryId>(); |
| + Iterable<DartType> get keys => _names.keys.toList(); |
| + |
| + JS.Statement _dischargeType(DartType type) { |
| + var name = _names.remove(type); |
| + if (name != null) { |
| + return (js.statement('let #;', [name])); |
|
Jennifer Messerly
2016/05/24 21:56:39
nit: parens not needed around return value
Leaf
2016/05/25 00:56:13
Done.
Leaf
2016/05/25 00:56:13
Done.
|
| + } |
| + return null; |
| + } |
| + |
| + /// Emit a list of statements declaring the cache variables for |
|
Jennifer Messerly
2016/05/24 21:56:39
doc comment style nit -- first sentence should be
Leaf
2016/05/25 00:56:13
Acknowledged.
|
| + /// types tracked by this table. If [typeFilter] is given, |
| + /// only emit the types listed in the filter. |
| + List<JS.Statement> discharge([Iterable<DartType> typeFilter]) { |
| + var decls = <JS.Statement>[]; |
| + var types = typeFilter ?? keys; |
| + for (var t in types) { |
| + var stmt = _dischargeType(t); |
| + if (stmt != null) decls.add(stmt); |
| + } |
| + return decls; |
| + } |
| + |
| + bool isNamed(DartType type) => _names.containsKey(type); |
| + |
| + /// If [type] is not already in the table, choose a new canonical |
| + /// variable to contain it. Emit an expression which uses [typeRep] to |
| + /// lazily initialize the cache in place. |
| + JS.Expression nameType(DartType type, JS.Expression typeRep) { |
| + var temp = _names[type]; |
|
Jennifer Messerly
2016/05/24 21:56:39
you could also do
var temp = _names.putIfAbse
Leaf
2016/05/25 00:56:13
Acknowledged.
|
| + if (temp == null) { |
| + ; |
|
Jennifer Messerly
2016/05/24 21:56:39
unintended empty statement?
Leaf
2016/05/25 00:56:13
Done.
|
| + _names[type] = temp = chooseTypeName(type); |
| + } |
| + return js.call('# || (# = #)', [temp, temp, typeRep]); |
| + } |
| + |
| + String _typeString(DartType type, {bool flat: false}) { |
| + if (type is ParameterizedType && type.name != null) { |
| + var clazz = type.name; |
| + var params = type.typeArguments; |
| + if (params == null) return clazz; |
| + if (params.every((p) => p.isDynamic)) return clazz; |
| + var paramStrings = params.map(_typeString); |
| + var paramString = paramStrings.join("\$"); |
| + return "${clazz}Of${paramString}"; |
| + } |
| + if (type is FunctionType) { |
| + if (flat) return "Fn"; |
| + var rType = _typeString(type.returnType, flat: true); |
| + var paramStrings = type.normalParameterTypes |
| + .take(3) |
| + .map((p) => _typeString(p, flat: true)); |
| + var paramString = paramStrings.join("And"); |
| + var count = type.normalParameterTypes.length; |
| + if (count > 3 || |
| + type.namedParameterTypes.isNotEmpty || |
| + type.optionalParameterTypes.isNotEmpty) { |
| + paramString = "${paramString}__"; |
| + } else if (count == 0) { |
| + paramString = "Void"; |
| + } |
| + return "${paramString}To${rType}"; |
| + } |
| + if (type is TypeParameterType) return type.name; |
| + if (type.name != null) return type.name; |
| + return "type"; |
| + } |
| + |
| + /// Heuristically choose a good name for the cache and generator |
| + /// variables. |
| + JS.Identifier chooseTypeName(DartType type) { |
| + return new JS.TemporaryId(_typeString(type)); |
| + } |
| +} |
| + |
| +/// _GeneratorTable tracks types which have been |
| +/// named and hoisted. |
| +class _GeneratorTable extends _CacheTable { |
| + final _defs = new Map<DartType, JS.Expression>(); |
| + |
| + _GeneratorTable(); |
|
Jennifer Messerly
2016/05/24 21:56:39
note: this is not needed
Leaf
2016/05/25 00:56:13
Done.
|
| + |
| + JS.Statement _dischargeType(DartType t) { |
| + var name = _names.remove(t); |
| + if (name != null) { |
| + JS.Expression init = _defs.remove(t); |
| + assert(init != null); |
| + return js.statement( |
| + 'let # = () => ((# = dart.constFn(#))());', [name, name, init]); |
| + } |
| + return null; |
| + } |
| + |
| + /// If [type] does not already have a generator name chosen for it, |
| + /// assign it one, using [typeRep] as the initializer for it. |
| + /// Emit an expression which calls the generator name. |
| + JS.Expression nameType(DartType type, JS.Expression typeRep) { |
|
Jennifer Messerly
2016/05/24 21:56:39
this looks mostly identical to the super method, m
Leaf
2016/05/25 00:56:13
It's 3 of 5 lines that are duplicated. I don't se
|
| + var temp = _names[type]; |
| + if (temp == null) { |
| + _names[type] = temp = chooseTypeName(type); |
| + _defs[type] = typeRep; |
| + } |
| + return js.call('#()', [temp]); |
| + } |
| +} |
| + |
| +class _TypeTable { |
| + /// Cache variable names for types emitted in place. |
| + final _cacheNames = new _CacheTable(); |
| + |
| + /// Cache variable names for definite function types emitted in place. |
| + final _definiteCacheNames = new _CacheTable(); |
| + |
| + /// Generator variable names for hoisted types. |
| + final _generators = new _GeneratorTable(); |
| + |
| + /// Generator variable names for hoisted definite function types. |
| + final _definiteGenerators = new _GeneratorTable(); |
| + |
| + /// Mapping from type parameters to the types which must have their |
| + /// cache/generator variables discharged at the binding site for the |
| + /// type variable since the type definition depends on the type |
| + /// parameter. |
| + final _scopeDependencies = new Map<TypeParameterElement, List<DartType>>(); |
| + |
| + /// Emit a list of statements declaring the cache variables and generator |
| + /// definitions tracked by the table. If [formals] is present, only |
| + /// emit the definitions which depend on the formals. |
| + List<JS.Statement> discharge([List<TypeParameterElement> formals]) { |
| + var filter = formals?.expand((p) => _scopeDependencies[p] ?? []); |
| + var stmts = [ |
| + _cacheNames, |
| + _definiteCacheNames, |
| + _generators, |
| + _definiteGenerators |
| + ].expand((c) => c.discharge(filter)).toList(); |
| + formals?.forEach(_scopeDependencies.remove); |
| + return stmts; |
| + } |
| + |
| + /// Record the dependencies of the type on its free variables |
| + bool recordScopeDependencies(DartType type) { |
| + var fvs = _freeTypeParameters(type); |
| + // TODO(leafp): This is a hack to avoid trying to hoist out of |
| + // generic functions and generic function types. This often degrades |
| + // readability to little or no benefit. It would be good to do this |
| + // when we know that we can hoist it to an outer scope, but for |
| + // now we just disable it. |
| + if (fvs.any((i) => i.enclosingElement is FunctionTypedElement)) { |
| + return true; |
| + } |
| + void addScope(TypeParameterElement i) { |
| + List<DartType> types = _scopeDependencies[i]; |
| + if (types == null) _scopeDependencies[i] = types = []; |
| + types.add(type); |
| + } |
| + fvs.forEach(addScope); |
| + return false; |
| + } |
| + |
| + /// Given a type [type], and a JS expression [typeRep] which implements it, |
| + /// Add the type and its representation to the table, returning an |
|
Jennifer Messerly
2016/05/24 21:56:39
nit: lowercase "add"
Leaf
2016/05/25 00:56:13
Done.
|
| + /// expression which implements the type (but which caches the value). |
| + /// If [hoist] is true, then the JS representation will be hoisted up |
|
Jennifer Messerly
2016/05/24 21:56:39
consider: splitting this into a few paragraphs?
Leaf
2016/05/25 00:56:13
Done.
|
| + /// as far as possible and shared between instances of the type. |
| + /// If [hoist] is false, the cache variable will be hoisted up as |
| + /// far as possible and shared between instances of the type, but the |
| + /// initializer expression will be emitted in place. |
| + /// The boolean parameter [definite] distinguishes between definite function |
| + /// types and other types (since the same DartType may have different |
| + /// representations as definite and indefinite function types). |
|
Jennifer Messerly
2016/05/24 21:56:39
I seem to recall at one point we had considered pu
Leaf
2016/05/25 00:56:13
Acknowledged.
|
| + JS.Expression nameType(DartType type, JS.Expression typeRep, bool hoist, |
|
Jennifer Messerly
2016/05/24 21:56:39
any reason to make "hoist" required and "definite"
Leaf
2016/05/25 00:56:13
Made them both named, asserted hoist is set. Defi
|
| + {bool definite: false}) { |
| + var table = hoist |
| + ? (definite ? _definiteGenerators : _generators) |
| + : (definite ? _definiteCacheNames : _cacheNames); |
| + if (!table.isNamed(type)) { |
| + if (recordScopeDependencies(type)) return typeRep; |
| + } |
| + return table.nameType(type, typeRep); |
| + } |
| +} |