Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1484263002: Use destructuring assignments for named parameters (#180) (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/closure/closure_annotator.dart ('k') | lib/src/js/builder.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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, SplayTreeSet; 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
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 17 matching lines...) Expand all
28 import '../info.dart'; 28 import '../info.dart';
29 import '../options.dart' show CodegenOptions; 29 import '../options.dart' show CodegenOptions;
30 import '../utils.dart'; 30 import '../utils.dart';
31 31
32 import 'code_generator.dart'; 32 import 'code_generator.dart';
33 import 'js_field_storage.dart'; 33 import 'js_field_storage.dart';
34 import 'js_interop.dart'; 34 import 'js_interop.dart';
35 import 'js_names.dart' as JS; 35 import 'js_names.dart' as JS;
36 import 'js_metalet.dart' as JS; 36 import 'js_metalet.dart' as JS;
37 import 'js_module_item_order.dart'; 37 import 'js_module_item_order.dart';
38 import 'js_names.dart';
38 import 'js_printer.dart' show writeJsLibrary; 39 import 'js_printer.dart' show writeJsLibrary;
39 import 'side_effect_analysis.dart'; 40 import 'side_effect_analysis.dart';
40 import 'package:collection/equality.dart'; 41 import 'package:collection/equality.dart';
41 42
42 // Various dynamic helpers we call. 43 // Various dynamic helpers we call.
43 // If renaming these, make sure to check other places like the 44 // If renaming these, make sure to check other places like the
44 // _runtime.js file and comments. 45 // _runtime.js file and comments.
45 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 46 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
46 // import and generate calls to, rather than dart_runtime.js 47 // import and generate calls to, rather than dart_runtime.js
47 const DPUT = 'dput'; 48 const DPUT = 'dput';
(...skipping 887 matching lines...) Expand 10 before | Expand all | Expand 10 after
935 936
936 var name = _constructorName(node.element); 937 var name = _constructorName(node.element);
937 938
938 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; 939 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
939 var redirect = node.redirectedConstructor; 940 var redirect = node.redirectedConstructor;
940 if (redirect != null) { 941 if (redirect != null) {
941 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; 942 var newKeyword = redirect.staticElement.isFactory ? '' : 'new';
942 // Pass along all arguments verbatim, and let the callee handle them. 943 // Pass along all arguments verbatim, and let the callee handle them.
943 // TODO(jmesserly): we'll need something different once we have 944 // TODO(jmesserly): we'll need something different once we have
944 // rest/spread support, but this should work for now. 945 // rest/spread support, but this should work for now.
945 var params = _visit(node.parameters); 946 var params =
947 _emitFormalParameterList(node.parameters, allowDestructuring: false);
948
946 var fun = js.call('function(#) { return $newKeyword #(#); }', 949 var fun = js.call('function(#) { return $newKeyword #(#); }',
947 [params, _visit(redirect), params,]) as JS.Fun; 950 [params, _visit(redirect), params]) as JS.Fun;
948 return annotate( 951 return annotate(
949 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 952 new JS.Method(name, fun, isStatic: true)..sourceInformation = node,
950 node.element); 953 node.element);
951 } 954 }
952 955
953 // Factory constructors are essentially static methods. 956 // Factory constructors are essentially static methods.
954 if (node.factoryKeyword != null) { 957 if (node.factoryKeyword != null) {
955 var body = <JS.Statement>[]; 958 var body = <JS.Statement>[];
956 var init = _emitArgumentInitializers(node, constructor: true); 959 var init = _emitArgumentInitializers(node, constructor: true);
957 if (init != null) body.add(init); 960 if (init != null) body.add(init);
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
1198 if (node is ConstructorDeclaration != constructor) return null; 1201 if (node is ConstructorDeclaration != constructor) return null;
1199 1202
1200 var parameters = _parametersOf(node); 1203 var parameters = _parametersOf(node);
1201 if (parameters == null) return null; 1204 if (parameters == null) return null;
1202 1205
1203 var body = <JS.Statement>[]; 1206 var body = <JS.Statement>[];
1204 for (var param in parameters.parameters) { 1207 for (var param in parameters.parameters) {
1205 var jsParam = _visit(param.identifier); 1208 var jsParam = _visit(param.identifier);
1206 1209
1207 if (param.kind == ParameterKind.NAMED) { 1210 if (param.kind == ParameterKind.NAMED) {
1208 // Parameters will be passed using their real names, not the (possibly 1211 if (!_isDestructurableNamedParam(param)) {
1209 // renamed) local variable. 1212 // Parameters will be passed using their real names, not the (possibly
1210 var paramName = js.string(param.identifier.name, "'"); 1213 // renamed) local variable.
1214 var paramName = js.string(param.identifier.name, "'");
1211 1215
1212 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming 1216 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
1213 // (either test if `obj.prop !== void 0`, or use JSCompiler_renameProper ty). 1217 body.add(js.statement('let # = # && # in # ? #.# : #;', [
1214 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 1218 jsParam,
1215 jsParam, 1219 _namedArgTemp,
1216 _namedArgTemp, 1220 paramName,
1217 paramName, 1221 _namedArgTemp,
1218 _namedArgTemp, 1222 _namedArgTemp,
1219 _namedArgTemp, 1223 paramName,
1220 paramName, 1224 _defaultParamValue(param),
1221 _defaultParamValue(param), 1225 ]));
1222 ])); 1226 }
1223 } else if (param.kind == ParameterKind.POSITIONAL) { 1227 } else if (param.kind == ParameterKind.POSITIONAL) {
1224 body.add(js.statement('if (# === void 0) # = #;', 1228 body.add(js.statement('if (# === void 0) # = #;',
1225 [jsParam, jsParam, _defaultParamValue(param)])); 1229 [jsParam, jsParam, _defaultParamValue(param)]));
1226 } 1230 }
1227 1231
1228 // TODO(jmesserly): various problems here, see: 1232 // TODO(jmesserly): various problems here, see:
1229 // https://github.com/dart-lang/dev_compiler/issues/161 1233 // https://github.com/dart-lang/dev_compiler/issues/161
1230 var paramType = param.element.type; 1234 var paramType = param.element.type;
1231 if (!constructor && _hasTypeParameter(paramType)) { 1235 if (!constructor && _hasTypeParameter(paramType)) {
1232 body.add(js.statement( 1236 body.add(js.statement(
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
1415 @override 1419 @override
1416 JS.Expression visitFunctionExpression(FunctionExpression node) { 1420 JS.Expression visitFunctionExpression(FunctionExpression node) {
1417 var params = _visit(node.parameters) as List<JS.Parameter>; 1421 var params = _visit(node.parameters) as List<JS.Parameter>;
1418 if (params == null) params = <JS.Parameter>[]; 1422 if (params == null) params = <JS.Parameter>[];
1419 1423
1420 var parent = node.parent; 1424 var parent = node.parent;
1421 var inStmt = parent.parent is FunctionDeclarationStatement; 1425 var inStmt = parent.parent is FunctionDeclarationStatement;
1422 if (parent is FunctionDeclaration) { 1426 if (parent is FunctionDeclaration) {
1423 return _emitFunctionBody(params, node.body); 1427 return _emitFunctionBody(params, node.body);
1424 } else { 1428 } else {
1425 String code; 1429 // Chrome Canary does not accept default values with destructuring in
1430 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
1431 // with regular functions (e.g. `function({a} = {}) { return 1 }`).
1432 // Note that Firefox accepts both syntaxes just fine.
1433 // TODO(ochafik): Simplify this code when Chrome Canary catches up.
1434 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam);
1435
1436 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }';
1426 JS.Node jsBody; 1437 JS.Node jsBody;
1427 var body = node.body; 1438 var body = node.body;
1428 if (body.isGenerator || body.isAsynchronous) { 1439 if (body.isGenerator || body.isAsynchronous) {
1429 code = '(#) => #';
1430 jsBody = _emitGeneratorFunctionBody(params, body); 1440 jsBody = _emitGeneratorFunctionBody(params, body);
1431 } else if (body is ExpressionFunctionBody) { 1441 } else if (body is ExpressionFunctionBody) {
1432 code = '(#) => #';
1433 jsBody = _visit(body.expression); 1442 jsBody = _visit(body.expression);
1434 } else { 1443 } else {
1435 code = '(#) => { #; }'; 1444 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }';
1436 jsBody = _visit(body); 1445 jsBody = _visit(body);
1437 } 1446 }
1438 var clos = js.call(code, [params, jsBody]); 1447 var clos = js.call(code, [params, jsBody]);
1439 if (!inStmt) { 1448 if (!inStmt) {
1440 var type = getStaticType(node); 1449 var type = getStaticType(node);
1441 return _emitFunctionTagged(clos, type, 1450 return _emitFunctionTagged(clos, type,
1442 topLevel: _executesAtTopLevel(node)); 1451 topLevel: _executesAtTopLevel(node));
1443 } 1452 }
1444 return clos; 1453 return clos;
1445 } 1454 }
(...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after
1966 return args; 1975 return args;
1967 } 1976 }
1968 1977
1969 @override 1978 @override
1970 JS.Property visitNamedExpression(NamedExpression node) { 1979 JS.Property visitNamedExpression(NamedExpression node) {
1971 assert(node.parent is ArgumentList); 1980 assert(node.parent is ArgumentList);
1972 return new JS.Property( 1981 return new JS.Property(
1973 _propertyName(node.name.label.name), _visit(node.expression)); 1982 _propertyName(node.name.label.name), _visit(node.expression));
1974 } 1983 }
1975 1984
1985 bool _isNamedParam(FormalParameter param) =>
1986 param.kind == ParameterKind.NAMED;
1987
1988 /// We cannot destructure named params that clash with JS reserved names:
1989 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392.
1990 bool _isDestructurableNamedParam(FormalParameter param) =>
1991 _isNamedParam(param) && !invalidVariableName(param.identifier.name);
1992
1976 @override 1993 @override
1977 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { 1994 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
1995 _emitFormalParameterList(node);
1996
1997 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
1998 {bool allowDestructuring: true}) {
1978 var result = <JS.Parameter>[]; 1999 var result = <JS.Parameter>[];
2000
2001 var namedVars = <JS.DestructuredVariable>[];
2002 var destructure = allowDestructuring &&
2003 node.parameters.where(_isNamedParam).every(_isDestructurableNamedParam);
2004 var hasNamedArgsConflictingWithObjectProperties = false;
2005 var needsOpts = false;
2006
1979 for (FormalParameter param in node.parameters) { 2007 for (FormalParameter param in node.parameters) {
1980 if (param.kind == ParameterKind.NAMED) { 2008 if (param.kind == ParameterKind.NAMED) {
1981 result.add(_namedArgTemp); 2009 if (destructure) {
1982 break; 2010 if (_jsObjectProperties.contains(param.identifier.name)) {
2011 hasNamedArgsConflictingWithObjectProperties = true;
2012 }
2013 namedVars.add(new JS.DestructuredVariable(
2014 name: _visit(param.identifier),
2015 defaultValue: _defaultParamValue(param)));
2016 } else {
2017 needsOpts = true;
2018 }
2019 } else {
2020 result.add(_visit(param));
1983 } 2021 }
1984 result.add(_visit(param)); 2022 }
2023
2024 if (needsOpts) {
2025 result.add(_namedArgTemp);
2026 } else if (namedVars.isNotEmpty) {
2027 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
2028 // in case there are conflicting names we create an object without
2029 // any prototype.
2030 var defaultOpts = hasNamedArgsConflictingWithObjectProperties
2031 ? js.call('Object.create(null)')
2032 : js.call('{}');
2033 result.add(new JS.DestructuredVariable(
2034 structure: new JS.ObjectBindingPattern(namedVars),
2035 defaultValue: defaultOpts));
1985 } 2036 }
1986 return result; 2037 return result;
1987 } 2038 }
1988 2039
2040 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`):
2041 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje ct-prototype-object
2042 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties- of-the-object.prototype-object
2043 static final Set<String> _jsObjectProperties = new Set<String>()
2044 ..addAll([
2045 "constructor",
2046 "toString",
2047 "toLocaleString",
2048 "valueOf",
2049 "hasOwnProperty",
2050 "isPrototypeOf",
2051 "propertyIsEnumerable",
2052 "__defineGetter__",
2053 "__lookupGetter__",
2054 "__defineSetter__",
2055 "__lookupSetter__",
2056 "__proto__"
2057 ]);
2058
1989 @override 2059 @override
1990 JS.Statement visitExpressionStatement(ExpressionStatement node) => 2060 JS.Statement visitExpressionStatement(ExpressionStatement node) =>
1991 _visit(node.expression).toStatement(); 2061 _visit(node.expression).toStatement();
1992 2062
1993 @override 2063 @override
1994 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => 2064 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) =>
1995 new JS.EmptyStatement(); 2065 new JS.EmptyStatement();
1996 2066
1997 @override 2067 @override
1998 JS.Statement visitAssertStatement(AssertStatement node) => 2068 JS.Statement visitAssertStatement(AssertStatement node) =>
(...skipping 1418 matching lines...) Expand 10 before | Expand all | Expand 10 after
3417 3487
3418 /// A special kind of element created by the compiler, signifying a temporary 3488 /// A special kind of element created by the compiler, signifying a temporary
3419 /// variable. These objects use instance equality, and should be shared 3489 /// variable. These objects use instance equality, and should be shared
3420 /// everywhere in the tree where they are treated as the same variable. 3490 /// everywhere in the tree where they are treated as the same variable.
3421 class TemporaryVariableElement extends LocalVariableElementImpl { 3491 class TemporaryVariableElement extends LocalVariableElementImpl {
3422 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3492 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3423 3493
3424 int get hashCode => identityHashCode(this); 3494 int get hashCode => identityHashCode(this);
3425 bool operator ==(Object other) => identical(this, other); 3495 bool operator ==(Object other) => identical(this, other);
3426 } 3496 }
OLDNEW
« no previous file with comments | « lib/src/closure/closure_annotator.dart ('k') | lib/src/js/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698