Skip to content

Commit 452b1ce

Browse files
committed
fix(parser): Allow access to Map properties in expressions
E.g. `map.keys`, `map.length`, etc. This is a proposed solution to dart-archive#394.
1 parent 259ac5b commit 452b1ce

File tree

5 files changed

+36
-4
lines changed

5 files changed

+36
-4
lines changed

bin/parser_generator_for_spec.dart

+5
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ main(arguments) {
101101
'items[1].name',
102102
'list[3] = 2',
103103
'map["square"] = 6',
104+
'map.isEmpty',
105+
'map.isNotEmpty',
106+
'map.keys',
107+
'map.length',
108+
'map.values',
104109
'method',
105110
'method()',
106111
'notAFn()',

lib/core/parser/eval_access.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ abstract class AccessReflective {
8080
if (holder == null) {
8181
_cachedKind = CACHED_VALUE;
8282
return _cachedValue = null;
83-
} else if (holder is Map) {
83+
} else if (holder is Map && !isMapProperty(name)) {
8484
_cachedKind = CACHED_MAP;
8585
_cachedValue = null;
8686
return holder[name];
@@ -204,7 +204,7 @@ abstract class AccessFast {
204204

205205
_eval(holder) {
206206
if (holder == null) return null;
207-
return (holder is Map) ? holder[name] : getter(holder);
207+
return (holder is Map && !isMapProperty(name)) ? holder[name] : getter(holder);
208208
}
209209

210210
_assign(scope, holder, value) {

lib/core/parser/utils.dart

+10
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,13 @@ setKeyed(object, key, value) {
9696
}
9797
return value;
9898
}
99+
100+
final List<String> _MAP_PROPERTIES = const <String>[
101+
"hashCode", // any reason to exclude this?
102+
"isEmpty",
103+
"isNotEmpty",
104+
"keys",
105+
"length",
106+
"runtimeType", // any reason to exclude this?
107+
"values"];
108+
bool isMapProperty(String key) => _MAP_PROPERTIES.contains(key);

lib/tools/parser_generator/dart_code_gen.dart

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ library dart_code_gen;
22

33
import 'package:angular/tools/reserved_dart_keywords.dart';
44
import 'package:angular/core/parser/syntax.dart';
5+
import 'package:angular/core/parser/utils.dart';
56

67
escape(String s) => s.replaceAllMapped(new RegExp(r'(\"|\$|\n)'), (m) {
78
var char = m[1];
@@ -11,7 +12,7 @@ escape(String s) => s.replaceAllMapped(new RegExp(r'(\"|\$|\n)'), (m) {
1112

1213
class DartCodeGen {
1314
final HelperMap getters = new HelperMap('_',
14-
getterTemplate, getterTemplateForReserved);
15+
getterTemplate, getterTemplateForReserved, getterTemplateWithoutMapTest);
1516
final HelperMap holders = new HelperMap('_ensure\$',
1617
holderTemplate, holderTemplateForReserved);
1718
final HelperMap setters = new HelperMap('_set\$',
@@ -223,14 +224,17 @@ class HelperMap {
223224
final String prefix;
224225
final Function template;
225226
final Function templateForReserved;
227+
final Function templateWithoutMapTest;
226228

227-
HelperMap(this.prefix, this.template, this.templateForReserved);
229+
HelperMap(this.prefix, this.template, this.templateForReserved, [this.templateWithoutMapTest]);
228230

229231
String lookup(String key) {
230232
String name = _computeName(key);
231233
if (helpers.containsKey(key)) return name;
232234
helpers[key] = isReserved(key)
233235
? templateForReserved(name, key)
236+
: isMapProperty(key) && templateWithoutMapTest != null
237+
? templateWithoutMapTest(name, key)
234238
: template(name, key);
235239
return name;
236240
}
@@ -261,6 +265,9 @@ $name(o) {
261265
}
262266
""";
263267

268+
String getterTemplateWithoutMapTest(String name, String key) => """
269+
$name(o) => o == null ? null : o.$key;
270+
""";
264271

265272
// ------------------------------------------------------------------
266273
// Templates for generated holders (getters for assignment).

test/core/parser/parser_spec.dart

+10
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,16 @@ main() {
647647
});
648648

649649

650+
it('should evaluate map item and field access', () {
651+
context['map'] = { 'a': 1.1, 'b': 'B' };
652+
expect(eval("map.isEmpty")).toEqual(false);
653+
expect(eval("map.isNotEmpty")).toEqual(true);
654+
expect(eval("map.keys")).toEqual(['a', 'b']);
655+
expect(eval("map.length")).toEqual(2);
656+
expect(eval("map.values")).toEqual([1.1, 'B']);
657+
});
658+
659+
650660
it('should evaluate JSON', () {
651661
expect(eval("[{}]")).toEqual([{}]);
652662
expect(eval("[{a:[]}, {b:1}]")).toEqual([{"a":[]},{"b":1}]);

0 commit comments

Comments
 (0)