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 | 2 |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
7 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 | 143 |
144 final ClassElement boolClass; | 144 final ClassElement boolClass; |
145 final ClassElement intClass; | 145 final ClassElement intClass; |
146 final ClassElement interceptorClass; | 146 final ClassElement interceptorClass; |
147 final ClassElement nullClass; | 147 final ClassElement nullClass; |
148 final ClassElement numClass; | 148 final ClassElement numClass; |
149 final ClassElement objectClass; | 149 final ClassElement objectClass; |
150 final ClassElement stringClass; | 150 final ClassElement stringClass; |
151 final ClassElement functionClass; | 151 final ClassElement functionClass; |
152 final ClassElement privateSymbolClass; | 152 final ClassElement privateSymbolClass; |
| 153 final PropertyAccessorElement _undefinedConstant; |
153 | 154 |
154 ConstFieldVisitor _constants; | 155 ConstFieldVisitor _constants; |
155 | 156 |
156 /// The current function body being compiled. | 157 /// The current function body being compiled. |
157 FunctionBody _currentFunction; | 158 FunctionBody _currentFunction; |
158 | 159 |
159 HashMap<TypeDefiningElement, AstNode> _declarationNodes; | 160 HashMap<TypeDefiningElement, AstNode> _declarationNodes; |
160 | 161 |
161 /// The stack of currently emitting elements, if generating top-level code | 162 /// The stack of currently emitting elements, if generating top-level code |
162 /// for them. This is not used when inside method bodies, because order does | 163 /// for them. This is not used when inside method bodies, because order does |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 dartCoreLibrary = _getLibrary(c, 'dart:core'), | 217 dartCoreLibrary = _getLibrary(c, 'dart:core'), |
217 boolClass = _getLibrary(c, 'dart:core').getType('bool'), | 218 boolClass = _getLibrary(c, 'dart:core').getType('bool'), |
218 intClass = _getLibrary(c, 'dart:core').getType('int'), | 219 intClass = _getLibrary(c, 'dart:core').getType('int'), |
219 numClass = _getLibrary(c, 'dart:core').getType('num'), | 220 numClass = _getLibrary(c, 'dart:core').getType('num'), |
220 nullClass = _getLibrary(c, 'dart:core').getType('Null'), | 221 nullClass = _getLibrary(c, 'dart:core').getType('Null'), |
221 objectClass = _getLibrary(c, 'dart:core').getType('Object'), | 222 objectClass = _getLibrary(c, 'dart:core').getType('Object'), |
222 stringClass = _getLibrary(c, 'dart:core').getType('String'), | 223 stringClass = _getLibrary(c, 'dart:core').getType('String'), |
223 functionClass = _getLibrary(c, 'dart:core').getType('Function'), | 224 functionClass = _getLibrary(c, 'dart:core').getType('Function'), |
224 privateSymbolClass = | 225 privateSymbolClass = |
225 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), | 226 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), |
226 dartJSLibrary = _getLibrary(c, 'dart:js') { | 227 dartJSLibrary = _getLibrary(c, 'dart:js'), |
| 228 _undefinedConstant = |
| 229 _getLibrary(c, 'dart:_runtime').publicNamespace.get('undefined') { |
| 230 assert(_undefinedConstant != null); |
227 typeRep = new JSTypeRep(rules, types); | 231 typeRep = new JSTypeRep(rules, types); |
228 } | 232 } |
229 | 233 |
230 Element get currentElement => _currentElements.last; | 234 Element get currentElement => _currentElements.last; |
231 | 235 |
232 LibraryElement get currentLibrary => currentElement.library; | 236 LibraryElement get currentLibrary => currentElement.library; |
233 | 237 |
234 /// The main entry point to JavaScript code generation. | 238 /// The main entry point to JavaScript code generation. |
235 /// | 239 /// |
236 /// Takes the metadata for the build unit, as well as resolved trees and | 240 /// Takes the metadata for the build unit, as well as resolved trees and |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
586 // per-compilation-unit state. Declarations can be visited out of order, | 590 // per-compilation-unit state. Declarations can be visited out of order, |
587 // this is only to catch things that haven't been emitted yet. | 591 // this is only to catch things that haven't been emitted yet. |
588 // | 592 // |
589 // See _emitTypeDeclaration. | 593 // See _emitTypeDeclaration. |
590 _currentElements.add(unit.element); | 594 _currentElements.add(unit.element); |
591 var isInternalSdk = isSdkInternalRuntime(currentLibrary); | 595 var isInternalSdk = isSdkInternalRuntime(currentLibrary); |
592 List<VariableDeclaration> fields; | 596 List<VariableDeclaration> fields; |
593 for (var declaration in unit.declarations) { | 597 for (var declaration in unit.declarations) { |
594 if (declaration is TopLevelVariableDeclaration) { | 598 if (declaration is TopLevelVariableDeclaration) { |
595 inferNullableTypes(declaration); | 599 inferNullableTypes(declaration); |
596 if (isInternalSdk && declaration.variables.isFinal) { | 600 if (isInternalSdk && |
| 601 (declaration.variables.isFinal || declaration.variables.isConst)) { |
597 _emitInternalSdkFields(declaration.variables.variables); | 602 _emitInternalSdkFields(declaration.variables.variables); |
598 } else { | 603 } else { |
599 (fields ??= []).addAll(declaration.variables.variables); | 604 (fields ??= []).addAll(declaration.variables.variables); |
600 } | 605 } |
601 continue; | 606 continue; |
602 } | 607 } |
603 | 608 |
604 if (fields != null) { | 609 if (fields != null) { |
605 _emitTopLevelFields(fields); | 610 _emitTopLevelFields(fields); |
606 fields = null; | 611 fields = null; |
(...skipping 1797 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2404 // than always when we visit the function body, so we control it explicitly. | 2409 // than always when we visit the function body, so we control it explicitly. |
2405 if (node is ConstructorDeclaration != constructor) return null; | 2410 if (node is ConstructorDeclaration != constructor) return null; |
2406 | 2411 |
2407 var parameters = _parametersOf(node); | 2412 var parameters = _parametersOf(node); |
2408 if (parameters == null) return null; | 2413 if (parameters == null) return null; |
2409 | 2414 |
2410 var body = <JS.Statement>[]; | 2415 var body = <JS.Statement>[]; |
2411 for (var param in parameters.parameters) { | 2416 for (var param in parameters.parameters) { |
2412 var jsParam = _emitSimpleIdentifier(param.identifier); | 2417 var jsParam = _emitSimpleIdentifier(param.identifier); |
2413 | 2418 |
2414 if (!options.destructureNamedParams) { | 2419 if (!options.destructureNamedParams && |
| 2420 param.kind != ParameterKind.REQUIRED) { |
2415 if (param.kind == ParameterKind.NAMED) { | 2421 if (param.kind == ParameterKind.NAMED) { |
2416 // Parameters will be passed using their real names, not the (possibly | 2422 // Parameters will be passed using their real names, not the (possibly |
2417 // renamed) local variable. | 2423 // renamed) local variable. |
2418 var paramName = js.string(param.identifier.name, "'"); | 2424 var paramName = js.string(param.identifier.name, "'"); |
2419 | 2425 var defaultValue = _defaultParamValue(param); |
2420 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. | 2426 if (defaultValue != null) { |
2421 body.add(js.statement('let # = # && # in # ? #.# : #;', [ | 2427 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. |
2422 jsParam, | 2428 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
2423 namedArgumentTemp, | 2429 jsParam, |
2424 paramName, | 2430 namedArgumentTemp, |
2425 namedArgumentTemp, | 2431 paramName, |
2426 namedArgumentTemp, | 2432 namedArgumentTemp, |
2427 paramName, | 2433 namedArgumentTemp, |
2428 _defaultParamValue(param), | 2434 paramName, |
2429 ])); | 2435 defaultValue, |
| 2436 ])); |
| 2437 } else { |
| 2438 body.add(js.statement('let # = # && #.#;', [ |
| 2439 jsParam, |
| 2440 namedArgumentTemp, |
| 2441 namedArgumentTemp, |
| 2442 paramName, |
| 2443 ])); |
| 2444 } |
2430 } else if (param.kind == ParameterKind.POSITIONAL) { | 2445 } else if (param.kind == ParameterKind.POSITIONAL) { |
2431 body.add(js.statement('if (# === void 0) # = #;', | 2446 var defaultValue = _defaultParamValue(param); |
2432 [jsParam, jsParam, _defaultParamValue(param)])); | 2447 if (defaultValue != null) { |
| 2448 body.add(js.statement( |
| 2449 'if (# === void 0) # = #;', [jsParam, jsParam, defaultValue])); |
| 2450 } |
2433 } | 2451 } |
2434 } | 2452 } |
2435 | 2453 |
2436 var paramElement = resolutionMap.elementDeclaredByFormalParameter(param); | 2454 var paramElement = resolutionMap.elementDeclaredByFormalParameter(param); |
2437 if (_isCovariant(paramElement)) { | 2455 if (_isCovariant(paramElement)) { |
2438 var castType = _emitType(paramElement.type); | 2456 var castType = _emitType(paramElement.type); |
2439 body.add(js.statement('#._check(#);', [castType, jsParam])); | 2457 body.add(js.statement('#._check(#);', [castType, jsParam])); |
2440 } | 2458 } |
2441 if (_annotatedNullCheck(paramElement)) { | 2459 if (_annotatedNullCheck(paramElement)) { |
2442 body.add(nullParameterCheck(jsParam)); | 2460 body.add(nullParameterCheck(jsParam)); |
2443 } | 2461 } |
2444 } | 2462 } |
2445 return body.isEmpty ? null : _statement(body); | 2463 return body.isEmpty ? null : _statement(body); |
2446 } | 2464 } |
2447 | 2465 |
2448 bool _isCovariant(ParameterElement p) { | 2466 bool _isCovariant(ParameterElement p) { |
2449 if (p.isCovariant) return true; | 2467 if (p.isCovariant) return true; |
2450 var covariantParams = _classProperties?.covariantParameters; | 2468 var covariantParams = _classProperties?.covariantParameters; |
2451 return covariantParams != null && covariantParams.contains(p); | 2469 return covariantParams != null && covariantParams.contains(p); |
2452 } | 2470 } |
2453 | 2471 |
2454 JS.Expression _defaultParamValue(FormalParameter param) { | 2472 JS.Expression _defaultParamValue(FormalParameter param) { |
2455 if (param is DefaultFormalParameter && param.defaultValue != null) { | 2473 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 2474 var defaultValue = param.defaultValue; |
| 2475 if (defaultValue is Identifier && |
| 2476 defaultValue.staticElement == _undefinedConstant) { |
| 2477 return null; |
| 2478 } |
2456 return _visit(param.defaultValue); | 2479 return _visit(param.defaultValue); |
2457 } else { | 2480 } else { |
2458 return new JS.LiteralNull(); | 2481 return new JS.LiteralNull(); |
2459 } | 2482 } |
2460 } | 2483 } |
2461 | 2484 |
2462 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { | 2485 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { |
2463 String name = | 2486 String name = |
2464 getAnnotationName(node.element, isJSAnnotation) ?? node.name.name; | 2487 getAnnotationName(node.element, isJSAnnotation) ?? node.name.name; |
2465 if (node.isGetter) { | 2488 if (node.isGetter) { |
(...skipping 1446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3912 } | 3935 } |
3913 JS.Expression name; | 3936 JS.Expression name; |
3914 JS.SimpleBindingPattern structure = null; | 3937 JS.SimpleBindingPattern structure = null; |
3915 String paramName = param.identifier.name; | 3938 String paramName = param.identifier.name; |
3916 if (JS.invalidVariableName(paramName)) { | 3939 if (JS.invalidVariableName(paramName)) { |
3917 name = js.string(paramName); | 3940 name = js.string(paramName); |
3918 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); | 3941 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); |
3919 } else { | 3942 } else { |
3920 name = _visit(param.identifier); | 3943 name = _visit(param.identifier); |
3921 } | 3944 } |
| 3945 |
| 3946 var defaultValue = _defaultParamValue(param); |
3922 namedVars.add(new JS.DestructuredVariable( | 3947 namedVars.add(new JS.DestructuredVariable( |
3923 name: name, | 3948 name: name, structure: structure, defaultValue: defaultValue)); |
3924 structure: structure, | |
3925 defaultValue: _defaultParamValue(param))); | |
3926 } else { | 3949 } else { |
3927 needsOpts = true; | 3950 needsOpts = true; |
3928 } | 3951 } |
3929 } else { | 3952 } else { |
3930 var jsParam = _visit(param); | 3953 var jsParam = _visit(param); |
3931 result.add(param is DefaultFormalParameter && destructure | 3954 var defaultValue = _defaultParamValue(param); |
| 3955 result.add(destructure && defaultValue != null |
3932 ? new JS.DestructuredVariable( | 3956 ? new JS.DestructuredVariable( |
3933 name: jsParam, defaultValue: _defaultParamValue(param)) | 3957 name: jsParam, defaultValue: defaultValue) |
3934 : jsParam); | 3958 : jsParam); |
3935 } | 3959 } |
3936 } | 3960 } |
3937 | 3961 |
3938 if (needsOpts) { | 3962 if (needsOpts) { |
3939 result.add(namedArgumentTemp); | 3963 result.add(namedArgumentTemp); |
3940 } else if (namedVars.isNotEmpty) { | 3964 } else if (namedVars.isNotEmpty) { |
3941 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so | 3965 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so |
3942 // in case there are conflicting names we create an object without | 3966 // in case there are conflicting names we create an object without |
3943 // any prototype. | 3967 // any prototype. |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4031 // A normal yield in a sync* | 4055 // A normal yield in a sync* |
4032 return jsExpr.toYieldStatement(star: star); | 4056 return jsExpr.toYieldStatement(star: star); |
4033 } | 4057 } |
4034 | 4058 |
4035 @override | 4059 @override |
4036 JS.Expression visitAwaitExpression(AwaitExpression node) { | 4060 JS.Expression visitAwaitExpression(AwaitExpression node) { |
4037 return new JS.Yield(_visit(node.expression)); | 4061 return new JS.Yield(_visit(node.expression)); |
4038 } | 4062 } |
4039 | 4063 |
4040 /// This is not used--we emit top-level fields as we are emitting the | 4064 /// This is not used--we emit top-level fields as we are emitting the |
4041 /// compilation unit, see [_emitCompilationUnit]. | 4065 /// compilation unit, see [visitCompilationUnit]. |
4042 @override | 4066 @override |
4043 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 4067 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
4044 assert(false); | 4068 assert(false); |
4045 } | 4069 } |
4046 | 4070 |
4047 /// This is not used--we emit fields as we are emitting the class, | 4071 /// This is not used--we emit fields as we are emitting the class, |
4048 /// see [visitClassDeclaration]. | 4072 /// see [visitClassDeclaration]. |
4049 @override | 4073 @override |
4050 visitFieldDeclaration(FieldDeclaration node) { | 4074 visitFieldDeclaration(FieldDeclaration node) { |
4051 assert(false); | 4075 assert(false); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4092 | 4116 |
4093 /// Emits a list of top-level field. | 4117 /// Emits a list of top-level field. |
4094 void _emitTopLevelFields(List<VariableDeclaration> fields) { | 4118 void _emitTopLevelFields(List<VariableDeclaration> fields) { |
4095 _moduleItems.add(_emitLazyFields(currentLibrary, fields)); | 4119 _moduleItems.add(_emitLazyFields(currentLibrary, fields)); |
4096 } | 4120 } |
4097 | 4121 |
4098 /// Treat dart:_runtime fields as safe to eagerly evaluate. | 4122 /// Treat dart:_runtime fields as safe to eagerly evaluate. |
4099 // TODO(jmesserly): it'd be nice to avoid this special case. | 4123 // TODO(jmesserly): it'd be nice to avoid this special case. |
4100 void _emitInternalSdkFields(List<VariableDeclaration> fields) { | 4124 void _emitInternalSdkFields(List<VariableDeclaration> fields) { |
4101 for (var field in fields) { | 4125 for (var field in fields) { |
| 4126 // Skip our magic undefined constant. |
| 4127 var element = field.element as TopLevelVariableElement; |
| 4128 if (element.getter == _undefinedConstant) continue; |
4102 _moduleItems.add(annotate( | 4129 _moduleItems.add(annotate( |
4103 js.statement('# = #;', | 4130 js.statement('# = #;', |
4104 [_emitTopLevelName(field.element), _visitInitializer(field)]), | 4131 [_emitTopLevelName(field.element), _visitInitializer(field)]), |
4105 field, | 4132 field, |
4106 field.element)); | 4133 field.element)); |
4107 } | 4134 } |
4108 } | 4135 } |
4109 | 4136 |
4110 JS.Expression _visitInitializer(VariableDeclaration node) { | 4137 JS.Expression _visitInitializer(VariableDeclaration node) { |
4111 var value = _annotatedNullCheck(node.element) | 4138 var value = _annotatedNullCheck(node.element) |
(...skipping 1925 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6037 // The library the prefix is referring to must come from a deferred import. | 6064 // The library the prefix is referring to must come from a deferred import. |
6038 var containingLibrary = resolutionMap | 6065 var containingLibrary = resolutionMap |
6039 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 6066 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
6040 .library; | 6067 .library; |
6041 var imports = containingLibrary.getImportsWithPrefix(prefix); | 6068 var imports = containingLibrary.getImportsWithPrefix(prefix); |
6042 return imports.length == 1 && imports[0].isDeferred; | 6069 return imports.length == 1 && imports[0].isDeferred; |
6043 } | 6070 } |
6044 | 6071 |
6045 bool _annotatedNullCheck(Element e) => | 6072 bool _annotatedNullCheck(Element e) => |
6046 e != null && findAnnotation(e, isNullCheckAnnotation) != null; | 6073 e != null && findAnnotation(e, isNullCheckAnnotation) != null; |
OLD | NEW |