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

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: Destructure function params directly (no more opts in most cases) 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
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 908 matching lines...) Expand 10 before | Expand all | Expand 10 after
919 919
920 var name = _constructorName(node.element); 920 var name = _constructorName(node.element);
921 921
922 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; 922 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
923 var redirect = node.redirectedConstructor; 923 var redirect = node.redirectedConstructor;
924 if (redirect != null) { 924 if (redirect != null) {
925 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; 925 var newKeyword = redirect.staticElement.isFactory ? '' : 'new';
926 // Pass along all arguments verbatim, and let the callee handle them. 926 // Pass along all arguments verbatim, and let the callee handle them.
927 // TODO(jmesserly): we'll need something different once we have 927 // TODO(jmesserly): we'll need something different once we have
928 // rest/spread support, but this should work for now. 928 // rest/spread support, but this should work for now.
929 var params = _visit(node.parameters); 929 var params = _visitFormalParameterList(
930 node.parameters, allowDestructuring: false);
Jennifer Messerly 2015/12/01 02:10:27 yeah, FYI, this makes total sense.
ochafik 2015/12/02 20:05:46 Acknowledged.
931
930 var fun = js.call('function(#) { return $newKeyword #(#); }', 932 var fun = js.call('function(#) { return $newKeyword #(#); }',
931 [params, _visit(redirect), params,]) as JS.Fun; 933 [params, _visit(redirect), params]) as JS.Fun;
932 return annotate( 934 return annotate(
933 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 935 new JS.Method(name, fun, isStatic: true)..sourceInformation = node,
934 node.element); 936 node.element);
935 } 937 }
936 938
937 // Factory constructors are essentially static methods. 939 // Factory constructors are essentially static methods.
938 if (node.factoryKeyword != null) { 940 if (node.factoryKeyword != null) {
939 var body = <JS.Statement>[]; 941 var body = <JS.Statement>[];
940 var init = _emitArgumentInitializers(node, constructor: true); 942 var init = _emitArgumentInitializers(node, constructor: true);
941 if (init != null) body.add(init); 943 if (init != null) body.add(init);
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after
1178 /// as generic type checks needed due to our covariance. 1180 /// as generic type checks needed due to our covariance.
1179 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { 1181 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) {
1180 // Constructor argument initializers are emitted earlier in the code, rather 1182 // Constructor argument initializers are emitted earlier in the code, rather
1181 // than always when we visit the function body, so we control it explicitly. 1183 // than always when we visit the function body, so we control it explicitly.
1182 if (node is ConstructorDeclaration != constructor) return null; 1184 if (node is ConstructorDeclaration != constructor) return null;
1183 1185
1184 var parameters = _parametersOf(node); 1186 var parameters = _parametersOf(node);
1185 if (parameters == null) return null; 1187 if (parameters == null) return null;
1186 1188
1187 var body = <JS.Statement>[]; 1189 var body = <JS.Statement>[];
1190
1191 Map<FormalParameter, JS.Identifier> paramNames =
Jennifer Messerly 2015/12/01 02:10:27 is this change still needed? the map doesn't seem
ochafik 2015/12/02 20:05:46 Indeed, removed :-)
1192 new Map.fromIterable(parameters.parameters,
1193 value: (param) => _visit(param.identifier));
1194
1188 for (var param in parameters.parameters) { 1195 for (var param in parameters.parameters) {
1189 var jsParam = _visit(param.identifier); 1196 var jsParam = paramNames[param];
1190 1197
1191 if (param.kind == ParameterKind.NAMED) { 1198 if (param.kind == ParameterKind.NAMED) {
1192 // Parameters will be passed using their real names, not the (possibly 1199 if (!_isDestructurableNamedParam(param)) {
1193 // renamed) local variable. 1200 // Parameters will be passed using their real names, not the (possibly
1194 var paramName = js.string(param.identifier.name, "'"); 1201 // renamed) local variable.
1202 var paramName = js.string(param.identifier.name, "'");
1195 1203
1196 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming 1204 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
1197 // (either test if `obj.prop !== void 0`, or use JSCompiler_renameProper ty). 1205 body.add(js.statement('let # = # && # in # ? #.# : #;', [
1198 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 1206 jsParam,
1199 jsParam, 1207 _namedArgTemp,
1200 _namedArgTemp, 1208 paramName,
1201 paramName, 1209 _namedArgTemp,
1202 _namedArgTemp, 1210 _namedArgTemp,
1203 _namedArgTemp, 1211 paramName,
1204 paramName, 1212 _defaultParamValue(param),
1205 _defaultParamValue(param), 1213 ]));
1206 ])); 1214 }
1207 } else if (param.kind == ParameterKind.POSITIONAL) { 1215 } else if (param.kind == ParameterKind.POSITIONAL) {
1208 body.add(js.statement('if (# === void 0) # = #;', 1216 body.add(js.statement('if (# === void 0) # = #;',
1209 [jsParam, jsParam, _defaultParamValue(param)])); 1217 [jsParam, jsParam, _defaultParamValue(param)]));
1210 } 1218 }
1211 1219
1212 // TODO(jmesserly): various problems here, see: 1220 // TODO(jmesserly): various problems here, see:
1213 // https://github.com/dart-lang/dev_compiler/issues/161 1221 // https://github.com/dart-lang/dev_compiler/issues/161
1214 var paramType = param.element.type; 1222 var paramType = param.element.type;
1215 if (!constructor && _hasTypeParameter(paramType)) { 1223 if (!constructor && _hasTypeParameter(paramType)) {
1216 body.add(js.statement( 1224 body.add(js.statement(
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
1347 @override 1355 @override
1348 JS.Expression visitFunctionExpression(FunctionExpression node) { 1356 JS.Expression visitFunctionExpression(FunctionExpression node) {
1349 var params = _visit(node.parameters) as List<JS.Parameter>; 1357 var params = _visit(node.parameters) as List<JS.Parameter>;
1350 if (params == null) params = <JS.Parameter>[]; 1358 if (params == null) params = <JS.Parameter>[];
1351 1359
1352 var parent = node.parent; 1360 var parent = node.parent;
1353 var inStmt = parent.parent is FunctionDeclarationStatement; 1361 var inStmt = parent.parent is FunctionDeclarationStatement;
1354 if (parent is FunctionDeclaration) { 1362 if (parent is FunctionDeclaration) {
1355 return _emitFunctionBody(params, node.body); 1363 return _emitFunctionBody(params, node.body);
1356 } else { 1364 } else {
1357 String code; 1365 // Chrome Canary does not accept default values with destructuring in
1366 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
1367 // with regular functions (e.g. `function({a} = {}) { return 1 }`).
1368 // Note that Firefox accepts both syntaxes just fine.
1369 // TODO(ochafik): Simplify this code when Chrome Canary catches up.
1370 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam);
1371
1372 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }';
1358 JS.Node jsBody; 1373 JS.Node jsBody;
1359 var body = node.body; 1374 var body = node.body;
1360 if (body.isGenerator || body.isAsynchronous) { 1375 if (body.isGenerator || body.isAsynchronous) {
1361 code = '(#) => #';
1362 jsBody = _emitGeneratorFunctionBody(params, body); 1376 jsBody = _emitGeneratorFunctionBody(params, body);
1363 } else if (body is ExpressionFunctionBody) { 1377 } else if (body is ExpressionFunctionBody) {
1364 code = '(#) => #';
1365 jsBody = _visit(body.expression); 1378 jsBody = _visit(body.expression);
1366 } else { 1379 } else {
1367 code = '(#) => { #; }'; 1380 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }';
1368 jsBody = _visit(body); 1381 jsBody = _visit(body);
1369 } 1382 }
1370 var clos = js.call(code, [params, jsBody]); 1383 var clos = js.call(code, [params, jsBody]);
1371 if (!inStmt) { 1384 if (!inStmt) {
1372 var type = getStaticType(node); 1385 var type = getStaticType(node);
1373 return _emitFunctionTagged(clos, type, 1386 return _emitFunctionTagged(clos, type,
1374 topLevel: _executesAtTopLevel(node)); 1387 topLevel: _executesAtTopLevel(node));
1375 } 1388 }
1376 return clos; 1389 return clos;
1377 } 1390 }
(...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after
1898 return args; 1911 return args;
1899 } 1912 }
1900 1913
1901 @override 1914 @override
1902 JS.Property visitNamedExpression(NamedExpression node) { 1915 JS.Property visitNamedExpression(NamedExpression node) {
1903 assert(node.parent is ArgumentList); 1916 assert(node.parent is ArgumentList);
1904 return new JS.Property( 1917 return new JS.Property(
1905 _propertyName(node.name.label.name), _visit(node.expression)); 1918 _propertyName(node.name.label.name), _visit(node.expression));
1906 } 1919 }
1907 1920
1921 static final Set<String> _jsObjectProperties = new Set<String>()..addAll([
Jennifer Messerly 2015/12/01 02:10:27 it'd be good to cite where this list comes from.
ochafik 2015/12/02 20:05:46 Done.
1922 "constructor",
1923 "toString",
1924 "toLocaleString",
1925 "valueOf",
1926 "hasOwnProperty",
1927 "isPrototypeOf",
1928 "propertyIsEnumerable",
1929 "__defineGetter__",
1930 "__lookupGetter__",
1931 "__defineSetter__",
1932 "__lookupSetter__",
1933 "__proto__"
1934 ]);
1935
1936 bool _isNamedParam(FormalParameter param) =>
1937 param.kind == ParameterKind.NAMED;
1938
1939 bool _isDestructurableNamedParam(FormalParameter param) {
Jennifer Messerly 2015/12/01 02:10:27 I think we can allow destructing always and just u
ochafik 2015/12/02 20:05:46 Oh sweet, thanks! Done. (removes lots of nonsense!
1940 var name = param.identifier.name;
1941 return _isNamedParam(param) &&
1942 !_jsObjectProperties.contains(name) &&
1943 name != 'super';
1944 }
1945
1946 _namedParams(FormalParameterList list) =>
1947 list.parameters.where(_isNamedParam);
1948
1949 bool _willDestructureNamedParams(FormalParameterList list) {
1950 var named = _namedParams(list).toList(growable: false);
1951 return named.isNotEmpty && named.every(_isDestructurableNamedParam);
1952 }
1953
1908 @override 1954 @override
1909 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { 1955 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
1956 _visitFormalParameterList(node, allowDestructuring: true);
1957
1958 List<JS.Parameter> _visitFormalParameterList(
Jennifer Messerly 2015/12/01 02:10:27 for better or worse, we have an "_emit" naming con
ochafik 2015/12/02 20:05:46 Done.
1959 FormalParameterList node, {bool allowDestructuring}) {
1960
1910 var result = <JS.Parameter>[]; 1961 var result = <JS.Parameter>[];
1962
1963 var namedVars = <JS.DestructuredVariable>[];
1964 var canDestructure = allowDestructuring && _willDestructureNamedParams(node) ;
Jennifer Messerly 2015/12/01 02:10:27 long line
ochafik 2015/12/02 20:05:46 Acknowledged.
1965 var needsOpts = false;
1966
1911 for (FormalParameter param in node.parameters) { 1967 for (FormalParameter param in node.parameters) {
1912 if (param.kind == ParameterKind.NAMED) { 1968 if (param.kind == ParameterKind.NAMED) {
1913 result.add(_namedArgTemp); 1969 if (canDestructure) {
1914 break; 1970 namedVars.add(new JS.DestructuredVariable(
1971 name: _visit(param.identifier),
1972 defaultValue: _defaultParamValue(param)));
1973 } else {
1974 needsOpts = true;
1975 }
1976 } else {
1977 result.add(_visit(param));
1915 } 1978 }
1916 result.add(_visit(param)); 1979 }
1980 if (canDestructure) {
1981 result.add(new JS.DestructuredVariable(
1982 structure: new JS.ObjectDestructuring(namedVars),
1983 defaultValue: js.call('{}')));
1984 } else if (needsOpts) {
1985 result.add(_namedArgTemp);
1917 } 1986 }
1918 return result; 1987 return result;
1919 } 1988 }
1920 1989
1921 @override 1990 @override
1922 JS.Statement visitExpressionStatement(ExpressionStatement node) => 1991 JS.Statement visitExpressionStatement(ExpressionStatement node) =>
1923 _visit(node.expression).toStatement(); 1992 _visit(node.expression).toStatement();
1924 1993
1925 @override 1994 @override
1926 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => 1995 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) =>
(...skipping 1420 matching lines...) Expand 10 before | Expand all | Expand 10 after
3347 3416
3348 /// A special kind of element created by the compiler, signifying a temporary 3417 /// A special kind of element created by the compiler, signifying a temporary
3349 /// variable. These objects use instance equality, and should be shared 3418 /// variable. These objects use instance equality, and should be shared
3350 /// everywhere in the tree where they are treated as the same variable. 3419 /// everywhere in the tree where they are treated as the same variable.
3351 class TemporaryVariableElement extends LocalVariableElementImpl { 3420 class TemporaryVariableElement extends LocalVariableElementImpl {
3352 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3421 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3353 3422
3354 int get hashCode => identityHashCode(this); 3423 int get hashCode => identityHashCode(this);
3355 bool operator ==(Object other) => identical(this, other); 3424 bool operator ==(Object other) => identical(this, other);
3356 } 3425 }
OLDNEW
« no previous file with comments | « lib/runtime/dart/mirrors.js ('k') | lib/src/js/builder.dart » ('j') | lib/src/js/builder.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698