| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dev_compiler.src.codegen.reify_coercions; | 5 library dev_compiler.src.codegen.reify_coercions; |
| 6 | 6 |
| 7 import 'package:analyzer/analyzer.dart' as analyzer; | 7 import 'package:analyzer/analyzer.dart' as analyzer; |
| 8 import 'package:analyzer/src/generated/ast.dart'; | 8 import 'package:analyzer/src/generated/ast.dart'; |
| 9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
| 10 import 'package:logging/logging.dart' as logger; | 10 import 'package:logging/logging.dart' as logger; |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 139 @override | 139 @override |
| 140 Object visitDownCast(DownCast node) { | 140 Object visitDownCast(DownCast node) { |
| 141 Expression castNode = _cm.coerceExpression(node.node, node.cast); | 141 Expression castNode = _cm.coerceExpression(node.node, node.cast); |
| 142 if (!NodeReplacer.replace(node, castNode)) { | 142 if (!NodeReplacer.replace(node, castNode)) { |
| 143 _log.severe("Failed to replace node for DownCast"); | 143 _log.severe("Failed to replace node for DownCast"); |
| 144 } | 144 } |
| 145 castNode.accept(this); | 145 castNode.accept(this); |
| 146 return null; | 146 return null; |
| 147 } | 147 } |
| 148 | 148 |
| 149 // TODO(leafp): Bind the coercions at the top level | |
| 150 @override | |
| 151 Object visitClosureWrapBase(ClosureWrapBase node) { | |
| 152 Expression newE = _cm.coerceExpression(node.node, node.wrapper); | |
| 153 if (!NodeReplacer.replace(node, newE)) { | |
| 154 _log.severe("Failed to replace node for Closure Wrap"); | |
| 155 } | |
| 156 newE.accept(this); | |
| 157 return null; | |
| 158 } | |
| 159 | |
| 160 Object visitCompilationUnit(CompilationUnit unit) { | 149 Object visitCompilationUnit(CompilationUnit unit) { |
| 161 _cm.enterCompilationUnit(unit); | 150 _cm.enterCompilationUnit(unit); |
| 162 Object ret = super.visitCompilationUnit(unit); | 151 Object ret = super.visitCompilationUnit(unit); |
| 163 _cm.exitCompilationUnit(unit); | 152 _cm.exitCompilationUnit(unit); |
| 164 return ret; | 153 return ret; |
| 165 } | 154 } |
| 166 | |
| 167 @override | |
| 168 Object visitClassDeclaration(ClassDeclaration cl) { | |
| 169 _cm.enterClass(); | |
| 170 Object ret = super.visitClassDeclaration(cl); | |
| 171 _cm.exitClass(cl); | |
| 172 return ret; | |
| 173 } | |
| 174 } | 155 } |
| 175 | 156 |
| 176 // This provides a placeholder variable manager. Currently it simply | 157 // This provides a placeholder variable manager. Currently it simply |
| 177 // mangles names in a way unlikely (but not guaranteed) to avoid | 158 // mangles names in a way unlikely (but not guaranteed) to avoid |
| 178 // collisions with user variables. | 159 // collisions with user variables. |
| 179 // TODO(leafp): Replace this with something real. | 160 // TODO(leafp): Replace this with something real. |
| 180 class VariableManager { | 161 class VariableManager { |
| 181 // TODO(leafp): Hack, not for real. | 162 // TODO(leafp): Hack, not for real. |
| 182 int _id = 0; | 163 int _id = 0; |
| 183 | 164 |
| 184 SimpleIdentifier freshIdentifier(String hint) { | 165 SimpleIdentifier freshIdentifier(String hint) { |
| 185 String n = _id.toString(); | 166 String n = _id.toString(); |
| 186 _id++; | 167 _id++; |
| 187 String s = "__$hint$n"; | 168 String s = "__$hint$n"; |
| 188 return AstBuilder.identifierFromString(s); | 169 return AstBuilder.identifierFromString(s); |
| 189 } | 170 } |
| 190 | 171 |
| 191 SimpleIdentifier freshTypeIdentifier(String hint) { | 172 SimpleIdentifier freshTypeIdentifier(String hint) { |
| 192 return freshIdentifier(hint); | 173 return freshIdentifier(hint); |
| 193 } | 174 } |
| 194 } | 175 } |
| 195 | 176 |
| 196 // This class manages the reification of coercions as dart code. Given a | 177 // This class manages the reification of coercions as dart code. Given a |
| 197 // coercion c and an expression e it will produce an expression e' which | 178 // coercion c and an expression e it will produce an expression e' which |
| 198 // is the result of coercing e using c. For closure wrappers, it maintains | 179 // is the result of coercing e using c. |
| 199 // a table of wrapper functions to be hoisted out to either the enclosing | |
| 200 // class level, or to the top level if not in a class (hoisting only to the | |
| 201 // class level avoids having to close over type variables, which is not | |
| 202 // easily done given the lack of generic functions). Generating the coercions | |
| 203 // inline is possible as well, but is quite a bit messier and harder to read | |
| 204 // since in general we need to bind the coerced expression to a lambda | |
| 205 // bound variable, both in order to deal with side-effects and to be | |
| 206 // able to properly record the return type of the wrapper function. | |
| 207 class CoercionManager { | 180 class CoercionManager { |
| 208 VariableManager _vm; | 181 VariableManager _vm; |
| 209 TypeManager _tm; | 182 TypeManager _tm; |
| 210 bool _hoistWrappers = false; | |
| 211 | 183 |
| 212 // A map containing all of the wrappers collected but not yet discharged | 184 CoercionManager(this._vm, this._tm); |
| 213 final Map<Identifier, Wrapper> _topWrappers = <Identifier, Wrapper>{}; | |
| 214 final Map<Identifier, Wrapper> _classWrappers = <Identifier, Wrapper>{}; | |
| 215 Map<Identifier, Wrapper> _wrappers; | |
| 216 | |
| 217 CoercionManager(this._vm, this._tm) { | |
| 218 _wrappers = _topWrappers; | |
| 219 } | |
| 220 | 185 |
| 221 // Call on entry to and exit from a compilation unit in order to properly | 186 // Call on entry to and exit from a compilation unit in order to properly |
| 222 // discharge the accumulated wrappers. | 187 // discharge the accumulated wrappers. |
| 223 void enterCompilationUnit(CompilationUnit unit) { | 188 void enterCompilationUnit(CompilationUnit unit) { |
| 224 _tm.enterCompilationUnit(unit); | 189 _tm.enterCompilationUnit(unit); |
| 225 _wrappers = _topWrappers; | |
| 226 } | 190 } |
| 227 void exitCompilationUnit(CompilationUnit unit) { | 191 void exitCompilationUnit(CompilationUnit unit) { |
| 228 for (Identifier i in _wrappers.keys) { | |
| 229 FunctionDeclaration f = _buildCoercion(i, _wrappers[i], true); | |
| 230 unit.declarations.add(f); | |
| 231 } | |
| 232 _wrappers.clear(); | |
| 233 _wrappers = _topWrappers; | |
| 234 _tm.exitCompilationUnit(unit); | 192 _tm.exitCompilationUnit(unit); |
| 235 } | 193 } |
| 236 | 194 |
| 237 // Call on entry to and exit from a class in order to properly | |
| 238 // discharge the accumulated wrappers. | |
| 239 void enterClass() { | |
| 240 _wrappers = _classWrappers; | |
| 241 } | |
| 242 void exitClass(ClassDeclaration cl) { | |
| 243 for (Identifier i in _wrappers.keys) { | |
| 244 ClassMember f = _buildCoercion(i, _wrappers[i], false); | |
| 245 cl.members.add(f); | |
| 246 } | |
| 247 _wrappers.clear(); | |
| 248 _wrappers = _topWrappers; | |
| 249 } | |
| 250 | |
| 251 // The main entry point. Coerce e using c, returning a new expression, | 195 // The main entry point. Coerce e using c, returning a new expression, |
| 252 // possibly recording additional coercions functions and typedefs to | 196 // possibly recording additional coercions functions and typedefs to |
| 253 // be discharged at a higher level. | 197 // be discharged at a higher level. |
| 254 Expression coerceExpression(Expression e, Coercion c) { | 198 Expression coerceExpression(Expression e, Coercion c) { |
| 255 assert(c != null); | 199 assert(c != null); |
| 256 assert(c is! CoercionError); | 200 assert(c is! CoercionError); |
| 257 if (e is NamedExpression) { | 201 if (e is NamedExpression) { |
| 258 Expression inner = coerceExpression(e.expression, c); | 202 Expression inner = coerceExpression(e.expression, c); |
| 259 return new NamedExpression(e.name, inner); | 203 return new NamedExpression(e.name, inner); |
| 260 } | 204 } |
| 261 if (c is Cast) return _castExpression(e, c); | 205 if (c is Cast) return _castExpression(e, c); |
| 262 if (c is Wrapper) return _wrapExpression(e, c); | |
| 263 assert(c is Identity); | 206 assert(c is Identity); |
| 264 return e; | 207 return e; |
| 265 } | 208 } |
| 266 | 209 |
| 267 ///////////////// Private ////////////////////////////////// | 210 ///////////////// Private ////////////////////////////////// |
| 268 | 211 |
| 269 Expression _wrapExpression(Expression e, Wrapper w) { | |
| 270 var q = _addWrapper(w); | |
| 271 var app = AstBuilder.application(q, <Expression>[e]); | |
| 272 app.staticType = w.toType; | |
| 273 return app; | |
| 274 } | |
| 275 | |
| 276 Expression _castExpression(Expression e, Cast c) { | 212 Expression _castExpression(Expression e, Cast c) { |
| 277 var ttName = _tm.typeNameFromDartType(c.toType); | 213 var ttName = _tm.typeNameFromDartType(c.toType); |
| 278 var cast = AstBuilder.asExpression(e, ttName); | 214 var cast = AstBuilder.asExpression(e, ttName); |
| 279 cast.staticType = c.toType; | 215 cast.staticType = c.toType; |
| 280 return cast; | 216 return cast; |
| 281 } | 217 } |
| 282 | |
| 283 Expression _addWrapper(Wrapper w) { | |
| 284 if (_hoistWrappers) { | |
| 285 var q = _vm.freshIdentifier("q"); | |
| 286 _wrappers[q] = w; | |
| 287 return q; | |
| 288 } else { | |
| 289 return _buildCoercionExpression(w); | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 // Choose a canonical name for the ith coercion parameter | |
| 294 // with name "name". | |
| 295 Identifier _coercionParameter(String name, int index) { | |
| 296 String s = name + index.toString(); | |
| 297 return AstBuilder.identifierFromString(s); | |
| 298 } | |
| 299 | |
| 300 List<FormalParameter> _wrapperFormalParameters(Wrapper wrapper) { | |
| 301 var namedParameters = wrapper.namedParameters; | |
| 302 var normalParameters = wrapper.normalParameters; | |
| 303 var optionalParameters = wrapper.optionalParameters; | |
| 304 var params = new List<FormalParameter>(); | |
| 305 for (int i = 0; i < normalParameters.length; i++) { | |
| 306 Identifier x = _coercionParameter("x", i); | |
| 307 // We use the toType to avoid changing the reified type | |
| 308 FormalParameter fp = _tm.typedFormal(x, normalParameters[i].toType); | |
| 309 params.add(AstBuilder.requiredFormal(fp)); | |
| 310 } | |
| 311 for (int i = 0; i < optionalParameters.length; i++) { | |
| 312 Identifier y = _coercionParameter("y", i); | |
| 313 // We use the toType to avoid changing the reified type | |
| 314 FormalParameter fp = _tm.typedFormal(y, optionalParameters[i].toType); | |
| 315 params.add(AstBuilder.optionalFormal(fp)); | |
| 316 } | |
| 317 for (String k in namedParameters.keys) { | |
| 318 // TODO(leafp): These could collide with the generated names. | |
| 319 Identifier z = AstBuilder.identifierFromString(k); | |
| 320 // We use the toType to avoid changing the reified type | |
| 321 FormalParameter fp = _tm.typedFormal(z, namedParameters[k].toType); | |
| 322 params.add(AstBuilder.namedFormal(fp)); | |
| 323 } | |
| 324 return params; | |
| 325 } | |
| 326 | |
| 327 List<Expression> _wrapperCoercedArguments(Wrapper wrapper) { | |
| 328 var namedParameters = wrapper.namedParameters; | |
| 329 var normalParameters = wrapper.normalParameters; | |
| 330 var optionalParameters = wrapper.optionalParameters; | |
| 331 var args = new List<Expression>(); | |
| 332 for (int i = 0; i < normalParameters.length; i++) { | |
| 333 Identifier x = _coercionParameter("x", i); | |
| 334 Expression e = coerceExpression(x, normalParameters[i]); | |
| 335 args.add(e); | |
| 336 } | |
| 337 for (int i = 0; i < optionalParameters.length; i++) { | |
| 338 Identifier y = _coercionParameter("y", i); | |
| 339 Expression e = coerceExpression(y, optionalParameters[i]); | |
| 340 args.add(e); | |
| 341 } | |
| 342 for (String k in namedParameters.keys) { | |
| 343 // TODO(leafp): These could collide with the generated names. | |
| 344 Identifier z = AstBuilder.identifierFromString(k); | |
| 345 Expression e = coerceExpression(z, namedParameters[k]); | |
| 346 args.add(AstBuilder.namedParameter(k, e)); | |
| 347 } | |
| 348 return args; | |
| 349 } | |
| 350 | |
| 351 // Given an identifier c, a value f and a coercion q : T0 -> T1 => S0 -> S1 | |
| 352 // wrap f using q and bind it to variable c | |
| 353 // T1 c(T0 x) => (f(x as T0) as S1) | |
| 354 // Note that we use the "fromType" to decorate the wrapper | |
| 355 // rather than the "toType" to avoid changing the reified type | |
| 356 // TODO(leafp): If we wrap non-function literals, we can still | |
| 357 // end up changing the runtime reified type, since we build a function | |
| 358 // literal based on the static type. | |
| 359 FunctionDeclarationStatement _wrapperMkInner( | |
| 360 Identifier c, Expression f, Wrapper wrapper) { | |
| 361 List<FormalParameter> params = _wrapperFormalParameters(wrapper); | |
| 362 List<Expression> args = _wrapperCoercedArguments(wrapper); | |
| 363 Expression app = AstBuilder.application(f, args); | |
| 364 Expression body = coerceExpression(app, wrapper.ret); | |
| 365 FunctionExpression ce = AstBuilder.expressionFunction(params, body, true); | |
| 366 TypeName rt = | |
| 367 _tm.typeNameFromDartType((wrapper.fromType as FunctionType).returnType); | |
| 368 Statement cDec = AstBuilder.functionDeclarationStatement(rt, c, ce); | |
| 369 return cDec; | |
| 370 } | |
| 371 | |
| 372 Tuple2<List<FormalParameter>, List<Statement>> _buildCoercionBody( | |
| 373 Wrapper wrapper) { | |
| 374 var f = AstBuilder.identifierFromString("f"); | |
| 375 var c = AstBuilder.identifierFromString("c"); | |
| 376 var cDec = _wrapperMkInner(c, f, wrapper); | |
| 377 var comp = AstBuilder.binaryExpression(f, "==", AstBuilder.nullLiteral()); | |
| 378 var n = AstBuilder.nullLiteral(); | |
| 379 var cond = AstBuilder.conditionalExpression(comp, n, c); | |
| 380 var stmts = <Statement>[cDec, AstBuilder.returnExpression(cond)]; | |
| 381 var fp = _tm.typedFormal(f, wrapper.fromType); | |
| 382 var params = <FormalParameter>[fp]; | |
| 383 return new Tuple2<List<FormalParameter>, List<Statement>>(params, stmts); | |
| 384 } | |
| 385 | |
| 386 Expression _buildCoercionExpression(Wrapper wrapper) { | |
| 387 var tup = _buildCoercionBody(wrapper); | |
| 388 return AstBuilder.blockFunction(tup.e0, tup.e1); | |
| 389 } | |
| 390 | |
| 391 // Given a name g and coercion q : T0 -> T1 => S0 -> S1 | |
| 392 // Bind g to a function or static method which maps | |
| 393 // T0 -> T1 functions to S0 -> S1 functions. | |
| 394 // T0 -> T1 g(S1 f(S0)) { | |
| 395 // T1 c(T0 x) => (f(x as T0) as S1); | |
| 396 // return (f == null) ? null : c; | |
| 397 // } | |
| 398 Declaration _buildCoercion(Identifier q, Wrapper wrapper, bool top) { | |
| 399 var tup = _buildCoercionBody(wrapper); | |
| 400 var params = tup.e0; | |
| 401 var stmts = tup.e1; | |
| 402 TypeName rt = _tm.typeNameFromDartType(wrapper.toType); | |
| 403 if (top) { | |
| 404 return AstBuilder.blockFunctionDeclaration(rt, q, params, stmts); | |
| 405 } else { | |
| 406 return AstBuilder.blockMethodDeclaration(rt, q, params, stmts); | |
| 407 } | |
| 408 } | |
| 409 } | 218 } |
| 410 | 219 |
| 411 // A class for managing the interaction between the DartType hierarchy | 220 // A class for managing the interaction between the DartType hierarchy |
| 412 // and the AST type representation. It provides utilities to translate | 221 // and the AST type representation. It provides utilities to translate |
| 413 // a DartType to AST. In order to do so, it maintains a map of typedefs | 222 // a DartType to AST. In order to do so, it maintains a map of typedefs |
| 414 // naming otherwise un-named types. These must be discharged at the top | 223 // naming otherwise un-named types. These must be discharged at the top |
| 415 // level of the compilation unit in order to produce well-formed dart code. | 224 // level of the compilation unit in order to produce well-formed dart code. |
| 416 // Note that in order to hoist the typedefs out of parameterized classes | 225 // Note that in order to hoist the typedefs out of parameterized classes |
| 417 // we must close over any type variables. | 226 // we must close over any type variables. |
| 418 class TypeManager { | 227 class TypeManager { |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 681 var t = _mkNewTypeName(dType, id, args); | 490 var t = _mkNewTypeName(dType, id, args); |
| 682 return t; | 491 return t; |
| 683 } | 492 } |
| 684 | 493 |
| 685 TypeName _mkNewTypeName(DartType type, Identifier id, List<TypeName> args) { | 494 TypeName _mkNewTypeName(DartType type, Identifier id, List<TypeName> args) { |
| 686 var t = AstBuilder.typeName(id, args); | 495 var t = AstBuilder.typeName(id, args); |
| 687 t.type = type; | 496 t.type = type; |
| 688 return t; | 497 return t; |
| 689 } | 498 } |
| 690 } | 499 } |
| OLD | NEW |