Chromium Code Reviews| 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.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 270 var type = node.element.type; | 270 var type = node.element.type; |
| 271 if (_pendingClasses.remove(node.element) == null) return null; | 271 if (_pendingClasses.remove(node.element) == null) return null; |
| 272 | 272 |
| 273 var name = type.name; | 273 var name = type.name; |
| 274 var result = js.statement('let # = dart.typedef(#, () => #);', [ | 274 var result = js.statement('let # = dart.typedef(#, () => #);', [ |
| 275 new JS.Identifier(name), | 275 new JS.Identifier(name), |
| 276 js.string(name, "'"), | 276 js.string(name, "'"), |
| 277 _emitTypeName(node.element.type, lowerTypedef: true) | 277 _emitTypeName(node.element.type, lowerTypedef: true) |
| 278 ]); | 278 ]); |
| 279 | 279 |
| 280 return _finishClassDef(type, result, null); | 280 return _finishClassDef(type, result); |
| 281 } | 281 } |
| 282 | 282 |
| 283 @override | 283 @override |
| 284 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 284 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
| 285 | 285 |
| 286 @override | 286 @override |
| 287 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 287 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
| 288 // If we've already emitted this class, skip it. | 288 // If we've already emitted this class, skip it. |
| 289 var type = node.element.type; | 289 var type = node.element.type; |
| 290 if (_pendingClasses.remove(node.element) == null) return null; | 290 if (_pendingClasses.remove(node.element) == null) return null; |
| 291 | 291 |
| 292 var name = node.name.name; | 292 var name = node.name.name; |
| 293 var heritage = | 293 var heritage = |
| 294 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 294 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
| 295 var classDecl = new JS.ClassDeclaration( | 295 var classDecl = new JS.ClassDeclaration( |
| 296 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); | 296 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); |
| 297 | 297 |
| 298 return _finishClassDef(type, classDecl, null); | 298 return _finishClassDef(type, classDecl); |
| 299 } | 299 } |
| 300 | 300 |
| 301 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { | 301 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { |
| 302 var jsTypeName = getConstantField(jsName, 'name', types.stringType); | 302 var jsTypeName = getConstantField(jsName, 'name', types.stringType); |
| 303 | 303 |
| 304 if (jsTypeName != null && jsTypeName != dartClassName) { | 304 if (jsTypeName != null && jsTypeName != dartClassName) { |
| 305 // We export the JS type as if it was a Dart type. For example this allows | 305 // We export the JS type as if it was a Dart type. For example this allows |
| 306 // `dom.InputElement` to actually be HTMLInputElement. | 306 // `dom.InputElement` to actually be HTMLInputElement. |
| 307 // TODO(jmesserly): if we had the JsName on the Element, we could just | 307 // TODO(jmesserly): if we had the JsName on the Element, we could just |
| 308 // generate it correctly when we refer to it. | 308 // generate it correctly when we refer to it. |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 333 (member.isStatic ? staticFields : fields).add(member); | 333 (member.isStatic ? staticFields : fields).add(member); |
| 334 } | 334 } |
| 335 } | 335 } |
| 336 | 336 |
| 337 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 337 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
| 338 _classHeritage(node), _emitClassMethods(node, ctors, fields)); | 338 _classHeritage(node), _emitClassMethods(node, ctors, fields)); |
| 339 | 339 |
| 340 var body = | 340 var body = |
| 341 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields); | 341 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields); |
| 342 | 342 |
| 343 var result = _finishClassDef(type, body); | |
| 344 | |
| 343 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); | 345 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); |
| 344 String jsPeerName = null; | |
| 345 if (jsPeer != null) { | 346 if (jsPeer != null) { |
| 346 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); | 347 var jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
| 348 if (jsPeerName != null) { | |
| 349 // TODO(jmesserly): this copies the dynamic members. | |
| 350 // Probably fine for objects coming from JS, but not if we actually | |
| 351 // want to support construction of instances with generic types other | |
| 352 // than dynamic. See issue #154 for Array and List<E> related bug. | |
| 353 var copyMembers = js.statement( | |
| 354 'dart.copyProperties(dart.global.#.prototype, #.prototype);', [ | |
|
Jennifer Messerly
2015/04/23 17:05:43
hmmm, it occurs to me we should assert !_lazyClass
Jacob
2015/04/23 17:09:14
make it so.
| |
| 355 _propertyName(jsPeerName), | |
| 356 classElem.name | |
| 357 ]); | |
| 358 return _statement([result, copyMembers]); | |
| 359 } | |
| 347 } | 360 } |
| 348 | 361 return result; |
| 349 return _finishClassDef(type, body, jsPeerName); | |
| 350 } | 362 } |
| 351 | 363 |
| 352 @override | 364 @override |
| 353 JS.Statement visitEnumDeclaration(EnumDeclaration node) => | 365 JS.Statement visitEnumDeclaration(EnumDeclaration node) => |
| 354 _unimplementedCall("Unimplemented enum: $node").toStatement(); | 366 _unimplementedCall("Unimplemented enum: $node").toStatement(); |
| 355 | 367 |
| 356 /// Given a class element and body, complete the class declaration. | 368 /// Given a class element and body, complete the class declaration. |
| 357 /// This handles generic type parameters, laziness (in library-cycle cases), | 369 /// This handles generic type parameters, laziness (in library-cycle cases), |
| 358 /// and ensuring dependencies are loaded first. | 370 /// and ensuring dependencies are loaded first. |
| 359 JS.Statement _finishClassDef( | 371 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { |
| 360 ParameterizedType type, JS.Statement body, String jsPeerName) { | |
| 361 var name = type.name; | 372 var name = type.name; |
| 362 var genericName = '$name\$'; | 373 var genericName = '$name\$'; |
| 363 | 374 |
| 364 JS.Statement genericDef; | 375 JS.Statement genericDef; |
| 365 JS.Expression genericInst; | 376 JS.Expression genericInst; |
| 366 if (type.typeParameters.isNotEmpty) { | 377 if (type.typeParameters.isNotEmpty) { |
| 367 genericDef = _emitGenericClassDef(type, body); | 378 genericDef = _emitGenericClassDef(type, body); |
| 368 var target = genericName; | 379 var target = genericName; |
| 369 if (_needQualifiedName(type.element)) { | 380 if (_needQualifiedName(type.element)) { |
| 370 target = js.call('#.#', [_exportsVar, genericName]); | 381 target = js.call('#.#', [_exportsVar, genericName]); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 409 var classDefs = <JS.Statement>[]; | 420 var classDefs = <JS.Statement>[]; |
| 410 if (type is InterfaceType) { | 421 if (type is InterfaceType) { |
| 411 _emitClassIfNeeded(classDefs, type.superclass); | 422 _emitClassIfNeeded(classDefs, type.superclass); |
| 412 for (var m in type.element.mixins) { | 423 for (var m in type.element.mixins) { |
| 413 _emitClassIfNeeded(classDefs, m); | 424 _emitClassIfNeeded(classDefs, m); |
| 414 } | 425 } |
| 415 } else if (type is FunctionType) { | 426 } else if (type is FunctionType) { |
| 416 _emitClassIfNeeded(classDefs, types.functionType); | 427 _emitClassIfNeeded(classDefs, types.functionType); |
| 417 } | 428 } |
| 418 classDefs.add(body); | 429 classDefs.add(body); |
| 419 if (jsPeerName != null) { | |
| 420 classDefs.add(js.statement( | |
| 421 'dart.copyProperties(dart.global.#.prototype, #.prototype);', [ | |
| 422 _propertyName(jsPeerName), | |
| 423 name | |
| 424 ])); | |
| 425 } | |
| 426 return _statement(classDefs); | 430 return _statement(classDefs); |
| 427 } | 431 } |
| 428 | 432 |
| 429 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { | 433 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { |
| 430 // We can only emit classes from this library. | 434 // We can only emit classes from this library. |
| 431 if (base.element.library != currentLibrary) return; | 435 if (base.element.library != currentLibrary) return; |
| 432 | 436 |
| 433 var baseNode = _pendingClasses[base.element]; | 437 var baseNode = _pendingClasses[base.element]; |
| 434 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); | 438 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); |
| 435 } | 439 } |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 635 var init = _visit(field.initializer); | 639 var init = _visit(field.initializer); |
| 636 if (init == null) init = new JS.LiteralNull(); | 640 if (init == null) init = new JS.LiteralNull(); |
| 637 body.add(js.statement('#.# = #;', [name, fieldName, init])); | 641 body.add(js.statement('#.# = #;', [name, fieldName, init])); |
| 638 } else { | 642 } else { |
| 639 lazyStatics.add(field); | 643 lazyStatics.add(field); |
| 640 } | 644 } |
| 641 } | 645 } |
| 642 } | 646 } |
| 643 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); | 647 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); |
| 644 if (lazy != null) body.add(lazy); | 648 if (lazy != null) body.add(lazy); |
| 645 | |
| 646 return _statement(body); | 649 return _statement(body); |
| 647 } | 650 } |
| 648 | 651 |
| 649 JS.Statement _overrideField(FieldElement e) { | 652 JS.Statement _overrideField(FieldElement e) { |
| 650 var cls = e.enclosingElement; | 653 var cls = e.enclosingElement; |
| 651 return js.statement('dart.virtualField(#, #)', [ | 654 return js.statement('dart.virtualField(#, #)', [ |
| 652 cls.name, | 655 cls.name, |
| 653 _emitMemberName(e.name, type: cls.type) | 656 _emitMemberName(e.name, type: cls.type) |
| 654 ]); | 657 ]); |
| 655 } | 658 } |
| (...skipping 1804 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2460 // TODO(jmesserly): validate the library. See issue #135. | 2463 // TODO(jmesserly): validate the library. See issue #135. |
| 2461 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2464 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
| 2462 | 2465 |
| 2463 bool _isJsPeerInterface(DartObjectImpl value) => | 2466 bool _isJsPeerInterface(DartObjectImpl value) => |
| 2464 value.type.name == 'JsPeerInterface'; | 2467 value.type.name == 'JsPeerInterface'; |
| 2465 | 2468 |
| 2466 // TODO(jacobr): we would like to do something like the following | 2469 // TODO(jacobr): we would like to do something like the following |
| 2467 // but we don't have summary support yet. | 2470 // but we don't have summary support yet. |
| 2468 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2471 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 2469 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2472 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| OLD | NEW |