Chromium Code Reviews| Index: lib/src/codegen/reify_coercions.dart |
| diff --git a/lib/src/codegen/reify_coercions.dart b/lib/src/codegen/reify_coercions.dart |
| index c8ad334dc0e3e63b0eb04cb7b841360f3cfaaccf..66013c9cfaa0e0191ee8aa8eed0c5702d90f0dbb 100644 |
| --- a/lib/src/codegen/reify_coercions.dart |
| +++ b/lib/src/codegen/reify_coercions.dart |
| @@ -12,6 +12,7 @@ import 'package:source_span/source_span.dart' show SourceFile; |
| import 'package:dev_compiler/src/checker/rules.dart'; |
| import 'package:dev_compiler/src/info.dart'; |
| +import 'package:dev_compiler/src/options.dart' show CompilerOptions; |
| import 'package:dev_compiler/src/utils.dart' as utils; |
| import 'ast_builder.dart'; |
| @@ -44,6 +45,14 @@ class _LocatedWrapper { |
| _LocatedWrapper(this.wrapper, this.loc); |
| } |
| +abstract class InstrumentedRuntime { |
| + Expression wrap(Expression coercion, Expression e, Expression fromType, |
| + Expression toType, Expression dartIs, String kind, String location); |
| + Expression cast(Expression e, Expression fromType, Expression toType, |
| + Expression dartIs, String kind, String location, bool ground); |
| + Expression type(Expression witnessFunction); |
| +} |
| + |
| class _Inference extends DownwardsInference { |
| TypeManager _tm; |
| @@ -82,8 +91,10 @@ class _Inference extends DownwardsInference { |
| var id = cName.type.name; |
| var typeName = AstBuilder.typeName(id, tNames); |
| cName.type = typeName; |
| - var rawType = (e.staticType.element as ClassElement).type; |
| - e.staticType = rawType.substitute4(targs); |
| + var newType = |
| + (e.staticType.element as ClassElement).type.substitute4(targs); |
| + e.staticType = newType; |
| + typeName.type = newType; |
| } |
| @override |
| @@ -105,16 +116,22 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| bool _skipCoercions = false; |
| final TypeRules _rules; |
| final _Inference _inferrer; |
| + final InstrumentedRuntime _runtime; |
| + final CompilerOptions _options; |
| - CoercionReifier._( |
| - this._cm, this._tm, this._vm, this._library, this._rules, this._inferrer); |
| + CoercionReifier._(this._cm, this._tm, this._vm, this._library, this._rules, |
| + this._inferrer, this._runtime, this._options); |
| - factory CoercionReifier(LibraryUnit library, TypeRules rules) { |
| + factory CoercionReifier( |
| + LibraryUnit library, TypeRules rules, CompilerOptions options, |
| + [InstrumentedRuntime runtime]) { |
| var vm = new VariableManager(); |
| - var tm = new TypeManager(library.library.element.enclosingElement, vm); |
| - var cm = new CoercionManager(vm, tm, rules); |
| + var tm = |
| + new TypeManager(library.library.element.enclosingElement, vm, runtime); |
| + var cm = new CoercionManager(vm, tm, rules, runtime); |
| var inferrer = new _Inference(rules, tm); |
| - return new CoercionReifier._(cm, tm, vm, library, rules, inferrer); |
| + return new CoercionReifier._( |
| + cm, tm, vm, library, rules, inferrer, runtime, options); |
| } |
| // This should be the entry point for this class. Entering via the |
| @@ -140,7 +157,7 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| final begin = e is AnnotatedNode |
| ? (e as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset |
| : e.offset; |
| - if (begin != 0) { |
| + if (begin != 0 && e.end > begin) { |
| var span = _file.span(begin, e.end); |
| var s = span.message("Cast"); |
| return s.substring(0, s.indexOf("Cast")); |
| @@ -163,6 +180,7 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| @override |
| Object visitAsExpression(AsExpression e) { |
| + if (_runtime == null) return super.visitAsExpression(e); |
| var cast = Coercion.cast(_rules.getStaticType(e.expression), e.type.type); |
| var loc = _locationInfo(e); |
| Expression castNode = |
| @@ -188,7 +206,7 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| @override |
| Object visitDownCast(DownCast node) { |
| - if (_skipCoercions) { |
| + if (_skipCoercions && !_options.allowConstCasts) { |
| _log.severe("Skipping runtime downcast in constant context"); |
| return null; |
| } |
| @@ -205,7 +223,7 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| // TODO(leafp): Bind the coercions at the top level |
| @override |
| Object visitClosureWrapBase(ClosureWrapBase node) { |
| - if (_skipCoercions) { |
| + if (_skipCoercions && !_options.allowConstCasts) { |
| _log.severe("Skipping coercion wrap in constant context"); |
| return null; |
| } |
| @@ -241,7 +259,7 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> |
| } |
| Object visitCompilationUnit(CompilationUnit unit) { |
| - _cm.enterCompilationUnit(); |
| + _cm.enterCompilationUnit(unit); |
| Object ret = super.visitCompilationUnit(unit); |
| _cm.exitCompilationUnit(unit); |
| return ret; |
| @@ -264,14 +282,14 @@ class VariableManager { |
| // TODO(leafp): Hack, not for real. |
| int _id = 0; |
| - Identifier freshIdentifier(String hint) { |
| + SimpleIdentifier freshIdentifier(String hint) { |
| String n = _id.toString(); |
| _id++; |
| String s = "__$hint$n"; |
| return AstBuilder.identifierFromString(s); |
|
Jennifer Messerly
2015/04/16 23:31:38
it'd be worth double checking how this identifier
Leaf
2015/04/17 20:49:36
The way JS code gen currently works, I don't think
|
| } |
| - Identifier freshTypeIdentifier(String hint) { |
| + SimpleIdentifier freshTypeIdentifier(String hint) { |
| return freshIdentifier(hint); |
| } |
| } |
| @@ -292,6 +310,7 @@ class CoercionManager { |
| TypeManager _tm; |
| bool _hoistWrappers = false; |
| TypeRules _rules; |
| + InstrumentedRuntime _runtime; |
| // A map containing all of the wrappers collected but not yet discharged |
| final Map<Identifier, _LocatedWrapper> _topWrappers = |
| @@ -300,14 +319,14 @@ class CoercionManager { |
| <Identifier, _LocatedWrapper>{}; |
| Map<Identifier, _LocatedWrapper> _wrappers; |
| - CoercionManager(this._vm, this._tm, this._rules) { |
| + CoercionManager(this._vm, this._tm, this._rules, [this._runtime]) { |
| _wrappers = _topWrappers; |
| } |
| // Call on entry to and exit from a compilation unit in order to properly |
| // discharge the accumulated wrappers. |
| - void enterCompilationUnit() { |
| - _tm.enterCompilationUnit(); |
| + void enterCompilationUnit(CompilationUnit unit) { |
| + _tm.enterCompilationUnit(unit); |
| _wrappers = _topWrappers; |
| } |
| void exitCompilationUnit(CompilationUnit unit) { |
| @@ -358,6 +377,11 @@ class CoercionManager { |
| Expression _wrapExpression(Expression e, Wrapper w, String k, String loc) { |
| var q = _addWrapper(w, loc); |
| + if (_runtime == null) { |
| + var app = AstBuilder.application(q, <Expression>[e]); |
| + app.staticType = w.toType; |
| + return app; |
| + } |
| var ttName = _tm.typeNameFromDartType(w.toType); |
| var tt = _tm.typeExpression(ttName); |
| var ft = _tm.typeExpressionFromDartType(w.fromType); |
| @@ -368,15 +392,18 @@ class CoercionManager { |
| var tup = _bindExpression("x", e); |
| var id = tup.e0; |
| var binder = tup.e1; |
| - var kind = AstBuilder.stringLiteral(k); |
| - var key = AstBuilder.multiLineStringLiteral(loc); |
| var dartIs = AstBuilder.isExpression(AstBuilder.parenthesize(id), ttName); |
| - var arguments = <Expression>[q, id, ft, tt, kind, key, dartIs]; |
| - return binder(new RuntimeOperation("wrap", arguments)); |
| + var oper = _runtime.wrap(q, id, ft, tt, dartIs, k, loc); |
| + return binder(oper); |
| } |
| Expression _castExpression(Expression e, Cast c, String k, String loc) { |
| var ttName = _tm.typeNameFromDartType(c.toType); |
| + if (_runtime == null) { |
| + var cast = AstBuilder.asExpression(e, ttName); |
| + cast.staticType = c.toType; |
| + return cast; |
| + } |
| var tt = _tm.typeExpression(ttName); |
| var ft = _tm.typeExpressionFromDartType(c.fromType); |
| if (c.fromType.element == null) { |
| @@ -390,12 +417,10 @@ class CoercionManager { |
| var tup = _bindExpression("x", e); |
| var id = tup.e0; |
| var binder = tup.e1; |
| - var kind = AstBuilder.stringLiteral(k); |
| - var key = AstBuilder.multiLineStringLiteral(loc); |
| var dartIs = AstBuilder.isExpression(AstBuilder.parenthesize(id), ttName); |
| - var ground = AstBuilder.booleanLiteral(_rules.isGroundType(c.toType)); |
| - var arguments = <Expression>[id, ft, tt, kind, key, dartIs, ground]; |
| - return binder(new RuntimeOperation("cast", arguments)); |
| + var ground = _rules.isGroundType(c.toType); |
| + var oper = _runtime.cast(id, ft, tt, dartIs, k, loc, ground); |
| + return binder(oper); |
| } |
| Expression _coerceExpression( |
| @@ -554,14 +579,19 @@ class TypeManager { |
| final VariableManager _vm; |
| final LibraryElement _currentLibrary; |
| final Map<Identifier, NewTypeIdDesc> addedTypes = {}; |
| + final InstrumentedRuntime _runtime; |
| + CompilationUnitElement _currentUnit; |
| /// A map containing new function typedefs to be introduced at the top level |
| /// This uses LinkedHashMap to emit code in a consistent order. |
| final Map<FunctionType, FunctionTypeAlias> _typedefs = {}; |
| - TypeManager(this._currentLibrary, this._vm); |
| + TypeManager(this._currentLibrary, this._vm, [this._runtime]); |
| + |
| + void enterCompilationUnit(CompilationUnit unit) { |
| + _currentUnit = unit.element; |
| + } |
| - void enterCompilationUnit() {} |
| void exitCompilationUnit(CompilationUnit unit) { |
| unit.declarations.addAll(_typedefs.values); |
| _typedefs.clear(); |
| @@ -631,16 +661,31 @@ class TypeManager { |
| var optionalParameters = type.optionalParameterTypes; |
| var params = new List<FormalParameter>(); |
| for (int i = 0; i < normalParameters.length; i++) { |
| - FormalParameter fp = _anonymousFormal(normalParameters[i]); |
| - params.add(AstBuilder.requiredFormal(fp)); |
| + FormalParameter fp = |
| + AstBuilder.requiredFormal(_anonymousFormal(normalParameters[i])); |
| + ParameterElementImpl fe = new ParameterElementImpl.forNode(fp.identifier); |
| + fe.parameterKind = fp.kind; |
| + fe.type = normalParameters[i]; |
| + fp.identifier.staticElement = fe; |
| + params.add(fp); |
| } |
| for (int i = 0; i < optionalParameters.length; i++) { |
| - FormalParameter fp = _anonymousFormal(optionalParameters[i]); |
| - params.add(AstBuilder.optionalFormal(fp)); |
| + FormalParameter fp = |
| + AstBuilder.optionalFormal(_anonymousFormal(optionalParameters[i])); |
| + ParameterElementImpl fe = new ParameterElementImpl.forNode(fp.identifier); |
|
Jennifer Messerly
2015/04/16 23:31:38
maybe try to encapsulate this? it seems like we're
Leaf
2015/04/17 20:49:35
Done.
|
| + fe.parameterKind = fp.kind; |
| + fe.type = optionalParameters[i]; |
| + fp.identifier.staticElement = fe; |
| + params.add(fp); |
| } |
| for (String k in namedParameters.keys) { |
| - FormalParameter fp = _anonymousFormal(namedParameters[k]); |
| - params.add(AstBuilder.namedFormal(fp)); |
| + FormalParameter fp = |
| + AstBuilder.namedFormal(_anonymousFormal(namedParameters[k])); |
| + ParameterElementImpl fe = new ParameterElementImpl.forNode(fp.identifier); |
| + fe.parameterKind = fp.kind; |
| + fe.type = namedParameters[k]; |
| + fp.identifier.staticElement = fe; |
| + params.add(fp); |
| } |
| return params; |
| } |
| @@ -666,7 +711,7 @@ class TypeManager { |
| return AstBuilder.simpleFormal(v, t); |
| } |
| - Identifier freshTypeDefVariable() { |
| + SimpleIdentifier freshTypeDefVariable() { |
| var t = _vm.freshTypeIdentifier("t"); |
| var desc = new NewTypeIdDesc( |
| fromCurrent: true, importedFrom: _currentLibrary, synthetic: true); |
| @@ -674,10 +719,10 @@ class TypeManager { |
| return t; |
| } |
| - Identifier typeParameterFromString(String name) => |
| + SimpleIdentifier typeParameterFromString(String name) => |
| AstBuilder.identifierFromString(name); |
| - Identifier freshReferenceToNamedType(DartType type) { |
| + SimpleIdentifier freshReferenceToNamedType(DartType type) { |
| var name = type.name; |
| assert(name != null); |
| var id = AstBuilder.identifierFromString(name); |
| @@ -694,6 +739,67 @@ class TypeManager { |
| return id; |
| } |
| + FunctionTypeAlias _newResolvedTypedef( |
| + FunctionType type, List<TypeParameterType> ftvs) { |
| + |
| + // The name of the typedef (unresolved at this point) |
|
Jennifer Messerly
2015/04/16 23:31:38
maybe a TODO for now and we can fix later, but: it
Leaf
2015/04/17 20:49:36
Suggestions as to meaningful names? I made the hi
|
| + SimpleIdentifier t = freshTypeDefVariable(); |
| + // The element for the new typedef |
| + var element = new FunctionTypeAliasElementImpl(t.name, 0); |
| + |
| + // Fresh type parameter identifiers for the free type variables |
| + List<Identifier> tNames = |
| + ftvs.map((x) => typeParameterFromString(x.name)).toList(); |
| + // The type parameters themselves |
| + List<TypeParameter> tps = tNames.map(AstBuilder.typeParameter).toList(); |
| + // Allocate the elements for the type parameters, fill in their |
| + // type (which makes no sense) and link up the various elements |
| + // For each type parameter identifier, make an element and a type |
| + // with that element, link the two together, set the identifier element |
| + // to that element, and the identifier type to that type. |
| + List<TypeParameterElement> tElements = tNames.map((x) { |
| + var element = new TypeParameterElementImpl(x.name, 0); |
| + var type = new TypeParameterTypeImpl(element); |
| + element.type = type; |
| + x.staticElement = element; |
| + x.staticType = type; |
| + return element; |
| + }).toList(); |
| + // Get the types out from the elements |
| + List<TypeParameterType> tTypes = tElements.map((x) => x.type).toList(); |
| + // Take the return type from the original type, and replace the free |
| + // type variables with the fresh type variables |
| + element.returnType = type.returnType.substitute2(tTypes, ftvs); |
| + // Set the type parameter elements |
| + element.typeParameters = tElements; |
| + // Set the parent element to the current compilation unit |
| + element.enclosingElement = _currentUnit; |
| + |
| + // This is the type corresponding to the typedef. Note that |
| + // almost all methods on this type delegate to the element, so it |
| + // cannot be safely be used for anything until the element is fully resolved |
| + FunctionTypeImpl substType = new FunctionTypeImpl.con2(element); |
| + element.type = substType; |
| + // Link the type and the element into the identifier for the typedef |
| + t.staticType = substType; |
| + t.staticElement = element; |
| + |
| + // Make the formal parameters for the typedef, using the original type |
| + // with the fresh type variables substituted in. |
| + List<FormalParameter> fps = |
| + _formalParameterListForFunctionType(type.substitute2(tTypes, ftvs)); |
| + // Get the static elements out of the parameters, and use them to |
| + // initialize the parameters in the element model |
| + element.parameters = fps.map((x) => x.identifier.staticElement).toList(); |
| + // Build the return type syntax |
| + TypeName ret = _typeNameFromDartType(substType.returnType); |
| + // This should now be fully resolved (or at least enough so for things |
| + // to work so far). |
| + FunctionTypeAlias alias = AstBuilder.functionTypeAlias(ret, t, tps, fps); |
| + |
| + return alias; |
| + } |
| + |
| // I think we can avoid alpha-varying type parameters, since |
| // the binding forms are so limited, so we just re-use the |
| // the original names for the formals and the actuals. |
| @@ -705,26 +811,19 @@ class TypeManager { |
| if (tpl != null) { |
| var ltp = tpl.typeParameters; |
| ts = new List<TypeName>.from( |
| - ltp.map((t) => _mkNewTypeName(t.name, null))); |
| + ltp.map((t) => _mkNewTypeName(null, t.name, null))); |
| } |
| var name = alias.name; |
| - return _mkNewTypeName(name, ts); |
| + return _mkNewTypeName(type, name, ts); |
| } |
| List<TypeParameterType> ftvs = _freeTypeVariables(type); |
| - Identifier t = freshTypeDefVariable(); |
| - |
| - Iterable<Identifier> tNames = |
| - ftvs.map((x) => typeParameterFromString(x.name)); |
| - List<TypeParameter> tps = tNames.map(AstBuilder.typeParameter).toList(); |
| - List<FormalParameter> fps = _formalParameterListForFunctionType(type); |
| - TypeName ret = _typeNameFromDartType(type.returnType); |
| - FunctionTypeAlias alias = AstBuilder.functionTypeAlias(ret, t, tps, fps); |
| - |
| + FunctionTypeAlias alias = _newResolvedTypedef(type, ftvs); |
| _typedefs[type] = alias; |
| List<TypeName> args = ftvs.map(_typeNameFromDartType).toList(); |
| - TypeName namedType = _mkNewTypeName(t, args); |
| + TypeName namedType = |
| + _mkNewTypeName(alias.name.staticType, alias.name, args); |
| return namedType; |
| } |
| @@ -735,8 +834,7 @@ class TypeManager { |
| if (dType is FunctionType) return _typeNameFromFunctionType(dType); |
| _log.severe("No name for type, casting through dynamic"); |
| var d = AstBuilder.identifierFromString("dynamic"); |
| - var t = _mkNewTypeName(d, null); |
| - t.type = dType; |
| + var t = _mkNewTypeName(dType, d, null); |
| return t; |
| } |
| SimpleIdentifier id = freshReferenceToNamedType(dType); |
| @@ -745,22 +843,23 @@ class TypeManager { |
| List<DartType> targs = dType.typeArguments; |
| args = targs.map(_typeNameFromDartType).toList(); |
| } |
| - var t = _mkNewTypeName(id, args); |
| - t.type = dType; |
| + var t = _mkNewTypeName(dType, id, args); |
| return t; |
| } |
| - TypeName _mkNewTypeName(Identifier id, List<TypeName> args) { |
| + TypeName _mkNewTypeName(DartType type, Identifier id, List<TypeName> args) { |
| var t = AstBuilder.typeName(id, args); |
| + t.type = type; |
| return t; |
| } |
| Expression _typeExpression(TypeName t) { |
| + assert(_runtime != null); |
| if (t.typeArguments != null && t.typeArguments.length > 0) { |
| var w = AstBuilder.identifierFromString("_"); |
| var fp = AstBuilder.simpleFormal(w, t); |
| var f = AstBuilder.blockFunction(<FormalParameter>[fp], <Statement>[]); |
| - return new RuntimeOperation("type", <Expression>[f]); |
| + return _runtime.type(f); |
| } |
| return t.name; |
| } |