Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 7ee8934

Browse files
committed
fix(parser): Makes the setter code more robust.
1 parent 4932b3f commit 7ee8934

File tree

2 files changed

+108
-11
lines changed

2 files changed

+108
-11
lines changed

lib/parser.dart

+13-11
Original file line numberDiff line numberDiff line change
@@ -216,22 +216,18 @@ setterChild(obj, childKey, value) {
216216

217217
InstanceMirror instanceMirror = reflect(obj);
218218
Symbol curSym = new Symbol(childKey);
219-
try {
220-
// maybe it is a member field?
221-
return instanceMirror.setField(curSym, value).reflectee;
222-
} catch (e) {
223-
throw "$e \n\n${e.stacktrace}";
224-
}
219+
// maybe it is a member field?
220+
return instanceMirror.setField(curSym, value).reflectee;
225221
}
226222

227223
setter(obj, path, setValue) {
228224
var element = path.split('.');
229225
for (var i = 0; element.length > 1; i++) {
230226
var key = element.removeAt(0);
231-
var propertyObj = obj[key];
227+
var propertyObj = getterChild(obj, key)[1];
232228
if (propertyObj == null) {
233229
propertyObj = {};
234-
obj[key] = propertyObj;
230+
setterChild(obj, key, propertyObj);
235231
}
236232
obj = propertyObj;
237233
}
@@ -494,7 +490,8 @@ class Parser {
494490
'at column ${t.index + 1} in';
495491
return 'Parser Error: $s $location [$text]';
496492
}
497-
evalError(String s) => 'Eval Error: $s while evaling [$text]';
493+
evalError(String s, [stack]) => 'Eval Error: $s while evaling [$text]' +
494+
(stack != null ? '\n\nFROM:\n$stack' : '');
498495

499496
Token peekToken() {
500497
if (tokens.length == 0)
@@ -680,8 +677,13 @@ class Parser {
680677
throw parserError('Expression ${tokensText(ts)} is not assignable', token);
681678
}
682679
right = logicalOR();
683-
return new ParsedFn((scope, locals) =>
684-
left.assign(scope, right(scope, locals), locals));
680+
return new ParsedFn((scope, locals) {
681+
try {
682+
return left.assign(scope, right(scope, locals), locals);
683+
} catch (e, s) {
684+
throw evalError('Caught $e', s);
685+
}
686+
});
685687
} else {
686688
return left;
687689
}

test/parser_spec.dart

+95
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,82 @@ main() {
428428
});
429429
});
430430

431+
describe('setters', () {
432+
it('should set a field in a map', () {
433+
scope['map'] = {};
434+
eval('map["square"] = 6');
435+
eval('map.dot = 7');
436+
437+
expect(scope['map']['square']).toEqual(6);
438+
expect(scope['map']['dot']).toEqual(7);
439+
});
440+
441+
442+
it('should set a field in a list', () {
443+
scope['list'] = [];
444+
eval('list[3] = 2');
445+
446+
expect(scope['list'].length).toEqual(4);
447+
expect(scope['list'][3]).toEqual(2);
448+
});
449+
450+
451+
it('should set a field on an object', () {
452+
scope['obj'] = new SetterObject();
453+
eval('obj.field = 1');
454+
455+
expect(scope['obj'].field).toEqual(1);
456+
});
457+
458+
459+
it('should set a setter on an object', () {
460+
scope['obj'] = new SetterObject();
461+
eval('obj.setter = 2');
462+
463+
expect(scope['obj'].setterValue).toEqual(2);
464+
});
465+
466+
467+
it('should set a []= on an object', () {
468+
scope['obj'] = new OverloadObject();
469+
eval('obj.overload = 7');
470+
471+
expect(scope['obj'].overloadValue).toEqual(7);
472+
});
473+
474+
475+
it('should set a field in a nested map on an object', () {
476+
scope['obj'] = new SetterObject();
477+
eval('obj.map.mapKey = 3');
478+
479+
expect(scope['obj'].map['mapKey']).toEqual(3);
480+
});
481+
482+
483+
it('should set a field in a nested object on an object', () {
484+
scope['obj'] = new SetterObject();
485+
eval('obj.nested.field = 1');
486+
487+
expect(scope['obj'].nested.field).toEqual(1);
488+
});
489+
490+
491+
it('should create a map for dotted acces', () {
492+
scope['obj'] = new SetterObject();
493+
eval('obj.field.key = 4');
494+
495+
expect(scope['obj'].field['key']).toEqual(4);
496+
});
497+
498+
499+
it('should throw a nice error for type mismatch', () {
500+
scope['obj'] = new SetterObject();
501+
expect(() {
502+
eval('obj.integer = "hello"');
503+
}).toThrow("Eval Error: Caught type 'String' is not a subtype of type 'int' of 'value'. while evaling [obj.integer = \"hello\"]");
504+
});
505+
});
506+
431507
describe('test cases imported from AngularJS', () {
432508
//// ==== IMPORTED ITs
433509
it('should parse expressions', () {
@@ -809,6 +885,25 @@ main() {
809885
});
810886
}
811887

888+
class SetterObject {
889+
var field;
890+
int integer;
891+
var map = {};
892+
893+
var nest;
894+
SetterObject get nested => nest != null ? nest : (nest = new SetterObject());
895+
896+
var setterValue;
897+
void set setter(x) { setterValue = x; }
898+
}
899+
900+
class OverloadObject {
901+
var overloadValue;
902+
operator []=(String name, var value) {
903+
overloadValue = value;
904+
}
905+
}
906+
812907
class ScopeWithErrors {
813908
String get boo { dump("got a boo"); throw "boo to you"; }
814909
String foo() { dump("got a foo"); throw "foo to you"; }

0 commit comments

Comments
 (0)