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 38 matching lines...) Loading... | |
49 final TypeRules rules; | 49 final TypeRules rules; |
50 | 50 |
51 /// The global extension method table. | 51 /// The global extension method table. |
52 final HashMap<String, List<InterfaceType>> _extensionMethods; | 52 final HashMap<String, List<InterfaceType>> _extensionMethods; |
53 | 53 |
54 /// The variable for the target of the current `..` cascade expression. | 54 /// The variable for the target of the current `..` cascade expression. |
55 SimpleIdentifier _cascadeTarget; | 55 SimpleIdentifier _cascadeTarget; |
56 /// The variable for the current catch clause | 56 /// The variable for the current catch clause |
57 SimpleIdentifier _catchParameter; | 57 SimpleIdentifier _catchParameter; |
58 | 58 |
59 ClassDeclaration currentClass; | |
60 ConstantEvaluator _constEvaluator; | 59 ConstantEvaluator _constEvaluator; |
61 | 60 |
62 final _exports = new Set<String>(); | 61 final _exports = new Set<String>(); |
63 final _lazyFields = <VariableDeclaration>[]; | 62 final _lazyFields = <VariableDeclaration>[]; |
64 final _properties = <FunctionDeclaration>[]; | 63 final _properties = <FunctionDeclaration>[]; |
65 final _privateNames = new HashMap<String, JSTemporary>(); | 64 final _privateNames = new HashMap<String, JSTemporary>(); |
66 final _pendingPrivateNames = <JSTemporary>[]; | 65 final _pendingPrivateNames = <JSTemporary>[]; |
67 final _extensionMethodNames = new HashSet<String>(); | 66 final _extensionMethodNames = new HashSet<String>(); |
68 final _pendingExtensionMethodNames = <String>[]; | 67 final _pendingExtensionMethodNames = <String>[]; |
69 final _temps = new HashMap<Element, JSTemporary>(); | 68 final _temps = new HashMap<Element, JSTemporary>(); |
(...skipping 246 matching lines...) Loading... | |
316 | 315 |
317 @override | 316 @override |
318 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 317 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
319 // If we've already emitted this class, skip it. | 318 // If we've already emitted this class, skip it. |
320 var type = node.element.type; | 319 var type = node.element.type; |
321 if (_pendingClasses.remove(node.element) == null) return null; | 320 if (_pendingClasses.remove(node.element) == null) return null; |
322 | 321 |
323 var jsName = getAnnotationValue(node, _isJsNameAnnotation); | 322 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
324 if (jsName != null) return _emitJsType(node.name.name, jsName); | 323 if (jsName != null) return _emitJsType(node.name.name, jsName); |
325 | 324 |
326 currentClass = node; | |
327 | |
328 var ctors = <ConstructorDeclaration>[]; | 325 var ctors = <ConstructorDeclaration>[]; |
329 var fields = <FieldDeclaration>[]; | 326 var fields = <FieldDeclaration>[]; |
330 var staticFields = <FieldDeclaration>[]; | 327 var staticFields = <FieldDeclaration>[]; |
331 for (var member in node.members) { | 328 for (var member in node.members) { |
332 if (member is ConstructorDeclaration) { | 329 if (member is ConstructorDeclaration) { |
333 ctors.add(member); | 330 ctors.add(member); |
334 } else if (member is FieldDeclaration) { | 331 } else if (member is FieldDeclaration) { |
335 (member.isStatic ? staticFields : fields).add(member); | 332 (member.isStatic ? staticFields : fields).add(member); |
336 } | 333 } |
337 } | 334 } |
338 | 335 |
339 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 336 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
340 _classHeritage(node), _emitClassMethods(node, ctors, fields)); | 337 _classHeritage(node), _emitClassMethods(node, ctors, fields)); |
341 | 338 |
342 var body = | 339 var body = |
343 _finishClassMembers(node.element, classExpr, ctors, staticFields); | 340 _finishClassMembers(node.element, classExpr, ctors, staticFields); |
344 currentClass = null; | |
345 | 341 |
346 return _finishClassDef(type, body); | 342 return _finishClassDef(type, body); |
347 } | 343 } |
348 | 344 |
349 @override | 345 @override |
350 JS.Statement visitEnumDeclaration(EnumDeclaration node) => | 346 JS.Statement visitEnumDeclaration(EnumDeclaration node) => |
351 _unimplementedCall("Unimplemented enum: $node").toStatement(); | 347 _unimplementedCall("Unimplemented enum: $node").toStatement(); |
352 | 348 |
353 /// Given a class element and body, complete the class declaration. | 349 /// Given a class element and body, complete the class declaration. |
354 /// This handles generic type parameters, laziness (in library-cycle cases), | 350 /// This handles generic type parameters, laziness (in library-cycle cases), |
(...skipping 627 matching lines...) Loading... | |
982 return new JS.Block([ | 978 return new JS.Block([ |
983 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), | 979 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), |
984 new JS.FunctionDeclaration(name, _visit(func.functionExpression)) | 980 new JS.FunctionDeclaration(name, _visit(func.functionExpression)) |
985 ]); | 981 ]); |
986 } | 982 } |
987 | 983 |
988 /// Writes a simple identifier. This can handle implicit `this` as well as | 984 /// Writes a simple identifier. This can handle implicit `this` as well as |
989 /// going through the qualified library name if necessary. | 985 /// going through the qualified library name if necessary. |
990 @override | 986 @override |
991 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { | 987 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { |
992 var e = node.staticElement; | 988 var accessor = node.staticElement; |
993 if (e == null) { | 989 if (accessor == null) { |
994 return js.commentExpression( | 990 return js.commentExpression( |
995 'Unimplemented unknown name', new JS.Identifier(node.name)); | 991 'Unimplemented unknown name', new JS.Identifier(node.name)); |
996 } | 992 } |
997 | 993 |
998 var variable = e is PropertyAccessorElement ? e.variable : e; | 994 // Get the original declaring element. If we had a property accessor, this |
Jennifer Messerly
2015/04/16 22:50:30
"variable" was misleading ... this is just the "re
| |
999 var name = variable.name; | 995 // indirects back to a (possibly synthetic) field. |
996 var element = accessor; | |
997 if (element is PropertyAccessorElement) element = accessor.variable; | |
Jennifer Messerly
2015/04/16 22:50:30
after this point we always use `element`.
| |
998 var name = element.name; | |
1000 | 999 |
1001 // library member | 1000 // library member |
1002 if (e.enclosingElement is CompilationUnitElement && | 1001 if (element.enclosingElement is CompilationUnitElement && |
1003 (e.library != libraryInfo.library || | 1002 (element.library != libraryInfo.library || |
1004 variable is TopLevelVariableElement && !variable.isConst)) { | 1003 element is TopLevelVariableElement && !element.isConst)) { |
1005 return js.call('#.#', [_libraryName(e.library), name]); | 1004 return js.call('#.#', [_libraryName(element.library), name]); |
1006 } | 1005 } |
1007 | 1006 |
1008 // instance member | 1007 // Unqualified class member. This could mean implicit-this, or implicit |
1009 if (currentClass != null && _needsImplicitThis(e)) { | 1008 // call to a static from the same class. |
1010 return js.call( | 1009 if (element is ClassMemberElement && element is! ConstructorElement) { |
1011 'this.#', _emitMemberName(name, type: currentClass.element.type)); | 1010 bool isStatic = element.isStatic; |
1012 } | 1011 var type = element.enclosingElement.type; |
1013 | 1012 |
1014 // static member | 1013 // For instance methods, we add implicit-this. |
1015 if (e is ExecutableElement && | 1014 // For static methods, we add the raw type name, without generics or |
1016 e.isStatic && | 1015 // library prefix. We don't need those because static calls can't use |
1017 variable.enclosingElement is ClassElement) { | 1016 // the generic type. |
1018 var className = (variable.enclosingElement as ClassElement).name; | 1017 var target = isStatic ? new JS.Identifier(type.name) : new JS.This(); |
1019 return js.call('#.#', [className, _emitMemberName(name, isStatic: true)]); | 1018 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
1019 return new JS.PropertyAccess(target, member); | |
1020 } | 1020 } |
1021 | 1021 |
1022 // initializing formal parameter, e.g. `Point(this.x)` | 1022 // initializing formal parameter, e.g. `Point(this.x)` |
1023 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { | 1023 if (element is ParameterElement && |
1024 element.isInitializingFormal && | |
1025 element.isPrivate) { | |
1024 /// Rename private names so they don't shadow the private field symbol. | 1026 /// Rename private names so they don't shadow the private field symbol. |
1025 /// The renamer would handle this, but it would prefer to rename the | 1027 /// The renamer would handle this, but it would prefer to rename the |
1026 /// temporary used for the private symbol. Instead rename the parameter. | 1028 /// temporary used for the private symbol. Instead rename the parameter. |
1027 return _getTemp(e, '${name.substring(1)}'); | 1029 return _getTemp(element, '${name.substring(1)}'); |
1028 } | 1030 } |
1029 | 1031 |
1030 if (_isTemporary(e)) { | 1032 if (_isTemporary(element)) { |
1031 if (name[0] == '#') { | 1033 if (name[0] == '#') { |
1032 return new JS.InterpolatedExpression(name.substring(1)); | 1034 return new JS.InterpolatedExpression(name.substring(1)); |
1033 } else { | 1035 } else { |
1034 return _getTemp(e, name); | 1036 return _getTemp(element, name); |
1035 } | 1037 } |
1036 } | 1038 } |
1037 | 1039 |
1038 return new JS.Identifier(name); | 1040 return new JS.Identifier(name); |
1039 } | 1041 } |
1040 | 1042 |
1041 JSTemporary _getTemp(Object key, String name) => | 1043 JSTemporary _getTemp(Object key, String name) => |
1042 _temps.putIfAbsent(key, () => new JSTemporary(name)); | 1044 _temps.putIfAbsent(key, () => new JSTemporary(name)); |
1043 | 1045 |
1044 JS.ArrayInitializer _emitTypeNames(List<DartType> types) { | 1046 JS.ArrayInitializer _emitTypeNames(List<DartType> types) { |
(...skipping 1235 matching lines...) Loading... | |
2280 | 2282 |
2281 /// Choose a canonical name from the library element. | 2283 /// Choose a canonical name from the library element. |
2282 /// This never uses the library's name (the identifier in the `library` | 2284 /// This never uses the library's name (the identifier in the `library` |
2283 /// declaration) as it doesn't have any meaningful rules enforced. | 2285 /// declaration) as it doesn't have any meaningful rules enforced. |
2284 JS.Identifier _libraryName(LibraryElement library) { | 2286 JS.Identifier _libraryName(LibraryElement library) { |
2285 if (library == libraryInfo.library) return _exportsVar; | 2287 if (library == libraryInfo.library) return _exportsVar; |
2286 return new JS.Identifier(jsLibraryName(library)); | 2288 return new JS.Identifier(jsLibraryName(library)); |
2287 } | 2289 } |
2288 | 2290 |
2289 DartType getStaticType(Expression e) => rules.getStaticType(e); | 2291 DartType getStaticType(Expression e) => rules.getStaticType(e); |
2290 | |
2291 static bool _needsImplicitThis(Element e) => | |
2292 e is PropertyAccessorElement && !e.variable.isStatic || | |
2293 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; | |
2294 } | 2292 } |
2295 | 2293 |
2296 class JSGenerator extends CodeGenerator { | 2294 class JSGenerator extends CodeGenerator { |
2297 final JSCodeOptions options; | 2295 final JSCodeOptions options; |
2298 | 2296 |
2299 /// For fast lookup of extension methods, we first check the name, then do a | 2297 /// For fast lookup of extension methods, we first check the name, then do a |
2300 /// (possibly expensive) subtype test to see if it matches one of the types | 2298 /// (possibly expensive) subtype test to see if it matches one of the types |
2301 /// that declares that method. | 2299 /// that declares that method. |
2302 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); | 2300 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); |
2303 | 2301 |
(...skipping 55 matching lines...) Loading... | |
2359 return filepath; | 2357 return filepath; |
2360 } | 2358 } |
2361 | 2359 |
2362 // TODO(jmesserly): validate the library. See issue #135. | 2360 // TODO(jmesserly): validate the library. See issue #135. |
2363 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2361 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2364 | 2362 |
2365 // TODO(jacobr): we would like to do something like the following | 2363 // TODO(jacobr): we would like to do something like the following |
2366 // but we don't have summary support yet. | 2364 // but we don't have summary support yet. |
2367 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2365 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2368 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2366 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |