Index: lib/src/codegen/js_typeref_codegen.dart |
diff --git a/lib/src/codegen/js_typeref_codegen.dart b/lib/src/codegen/js_typeref_codegen.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f6f5d61e0c5fc416291703623f2f75c616ac0304 |
--- /dev/null |
+++ b/lib/src/codegen/js_typeref_codegen.dart |
@@ -0,0 +1,128 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of js_codegen; |
+ |
+/// Mixin with logic to generate [TypeRef]s out of [DartType]s. |
+abstract class JsTypeRefCodegen { |
+ final _resolved = <DartType, JS.TypeRef>{}; |
+ |
+ // Mixin dependencies: |
+ CodegenOptions get options; |
+ TypeProvider get types; |
+ JS.Identifier get _namedArgTemp; |
+ LibraryElement get currentLibrary; |
+ JS.Identifier _libraryName(LibraryElement e); |
+ String _getJSExportName(Element e); |
+ |
+ /// Finds the qualified path to the type. |
+ JS.TypeRef _emitTopLevelTypeRef(DartType type) { |
+ var e = type.element; |
+ if (e.library == currentLibrary) { |
+ return new JS.TypeRef.named(type.name); |
+ } else { |
+ return new JS.TypeRef.qualified([ |
+ _libraryName(e.library), |
+ new JS.Identifier(_getJSExportName(e) ?? type.name) |
+ ]); |
+ } |
+ } |
+ |
+ JS.TypeRef emitTypeRef(DartType type) { |
+ if (!options.closure) return null; |
+ |
+ return _resolved.putIfAbsent(type, () { |
+ if (type == null) new JS.TypeRef.unknown(); |
+ // TODO(ochafik): Consider calling _loader.declareBeforeUse(type.element). |
+ if (type.isBottom || type.isDynamic) new JS.TypeRef.any(); |
+ if (type.isVoid) return new JS.TypeRef.void_(); |
+ |
+ if (type == types.intType) return new JS.TypeRef.number().orNull(); |
+ if (type == types.numType) return new JS.TypeRef.number().orNull(); |
+ if (type == types.doubleType) return new JS.TypeRef.number().orNull(); |
+ if (type == types.boolType) return new JS.TypeRef.boolean().orNull(); |
+ if (type == types.stringType) return new JS.TypeRef.string(); |
+ |
+ if (type is TypeParameterType) return new JS.TypeRef.named(type.name); |
+ if (type is ParameterizedType) { |
+ JS.TypeRef rawType; |
+ if (type is FunctionType && type.name == null) { |
+ var args = <JS.Identifier, JS.TypeRef>{}; |
+ for (var param in type.parameters) { |
+ if (param.parameterKind == ParameterKind.NAMED) break; |
+ var type = emitTypeRef(param.type); |
+ args[new JS.Identifier(param.name)] = param.parameterKind == |
+ ParameterKind.POSITIONAL ? type.toOptional() : type; |
+ } |
+ var namedParamType = emitNamedParamsArgType(type.parameters); |
+ if (namedParamType != null) { |
+ args[_namedArgTemp] = namedParamType.toOptional(); |
+ } |
+ |
+ rawType = new JS.TypeRef.function(emitTypeRef(type.returnType), args); |
+ } else { |
+ var jsTypeRef = _getDartJsTypeRef(type); |
+ if (jsTypeRef != null) return jsTypeRef; |
+ |
+ rawType = _emitTopLevelTypeRef(type); |
+ } |
+ var typeArgs = _getOwnTypeArguments(type).map(emitTypeRef); |
+ return typeArgs.isEmpty |
+ ? rawType |
+ : new JS.TypeRef.generic(rawType, typeArgs); |
+ } |
+ return new JS.TypeRef.unknown(); |
+ }); |
+ } |
+ |
+ JS.TypeRef emitNamedParamsArgType(List<ParameterElement> params) { |
+ if (!options.closure) return null; |
+ |
+ var namedArgs = <JS.Identifier, JS.TypeRef>{}; |
+ for (ParameterElement param in params) { |
+ if (param.parameterKind != ParameterKind.NAMED) continue; |
+ namedArgs[new JS.Identifier(param.name)] = |
+ emitTypeRef(param.type).toOptional(); |
+ } |
+ if (namedArgs.isEmpty) return null; |
+ return new JS.TypeRef.record(namedArgs); |
+ } |
+ |
+ /// Gets the "own" type arguments of [type]. |
+ /// |
+ /// Method argument with adhoc unnamed [FunctionType] inherit any type params |
+ /// from their enclosing class: |
+ /// |
+ /// class Foo<T> { |
+ /// void method(f()); // f has [T] as type arguments, |
+ /// } // but [] as its "own" type arguments. |
+ Iterable<DartType> _getOwnTypeArguments(ParameterizedType type) sync* { |
+ for (int i = 0, n = type.typeParameters.length; i < n; i++) { |
+ if (type.typeParameters[i].enclosingElement == type.element) { |
+ yield type.typeArguments[i]; |
+ } |
+ } |
+ } |
+ |
+ /// Special treatment of types from dart:js |
+ /// TODO(ochafik): Is this the right thing to do? And what about package:js? |
+ JS.TypeRef _getDartJsTypeRef(DartType type) { |
+ switch (type.element.source.uri.toString()) { |
+ case 'dart:js': |
+ switch (type.name) { |
+ case 'JsArray': |
+ return new JS.TypeRef.array( |
+ type is InterfaceType && type.typeArguments.length == 1 |
+ ? emitTypeRef(type.typeArguments.single) |
+ : null); |
+ case 'JsObject': |
+ return new JS.TypeRef.object(); |
+ case 'JsFunction': |
+ return new JS.TypeRef.function(); |
+ } |
+ break; |
+ } |
+ return null; |
+ } |
+} |