fix($parse): treat falsy values as defined in assignment expressions

Closes #14990
Closes #14994
This commit is contained in:
mohamed amr
2016-09-11 20:31:14 +02:00
committed by Lucas Mirelmann
parent 606ea5d23e
commit 4f44e01894
2 changed files with 74 additions and 4 deletions
+8 -4
View File
@@ -944,7 +944,7 @@ ASTCompiler.prototype = {
self.if_(self.stage === 'inputs' || 's', function() {
if (create && create !== 1) {
self.if_(
self.not(self.nonComputedMember('s', ast.name)),
self.isNull(self.nonComputedMember('s', ast.name)),
self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
}
self.assign(intoId, self.nonComputedMember('s', ast.name));
@@ -973,7 +973,7 @@ ASTCompiler.prototype = {
}
} else {
if (create && create !== 1) {
self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
}
expression = self.nonComputedMember(left, ast.property.name);
self.assign(intoId, expression);
@@ -1155,6 +1155,10 @@ ASTCompiler.prototype = {
return '!(' + expression + ')';
},
isNull: function(expression) {
return expression + '==null';
},
notNull: function(expression) {
return expression + '!=null';
},
@@ -1546,7 +1550,7 @@ ASTInterpreter.prototype = {
identifier: function(name, context, create, expression) {
return function(scope, locals, assign, inputs) {
var base = locals && (name in locals) ? locals : scope;
if (create && create !== 1 && base && !(base[name])) {
if (create && create !== 1 && base && base[name] == null) {
base[name] = {};
}
var value = base ? base[name] : undefined;
@@ -1583,7 +1587,7 @@ ASTInterpreter.prototype = {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
if (create && create !== 1) {
if (lhs && !(lhs[right])) {
if (lhs && lhs[right] == null) {
lhs[right] = {};
}
}
+66
View File
@@ -3155,6 +3155,72 @@ describe('parser', function() {
expect(isFunction(s.toString)).toBe(true);
expect(l.toString).toBe(1);
}));
it('should overwrite undefined / null scope properties when assigning', inject(function($parse) {
var scope;
scope = {};
$parse('a.b = 1')(scope);
$parse('c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
scope = {a: {}};
$parse('a.b.c = 1')(scope);
$parse('a.c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
scope = {a: undefined, c: undefined};
$parse('a.b = 1')(scope);
$parse('c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
scope = {a: {b: undefined, c: undefined}};
$parse('a.b.c = 1')(scope);
$parse('a.c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
scope = {a: null, c: null};
$parse('a.b = 1')(scope);
$parse('c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
scope = {a: {b: null, c: null}};
$parse('a.b.c = 1')(scope);
$parse('a.c["d"] = 2')(scope);
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
}));
they('should not overwrite $prop scope properties when assigning', [0, false, '', NaN],
function(falsyValue) {
inject(function($parse) {
var scope;
scope = {a: falsyValue, c: falsyValue};
tryParseAndIgnoreException('a.b = 1');
tryParseAndIgnoreException('c["d"] = 2');
expect(scope).toEqual({a: falsyValue, c: falsyValue});
scope = {a: {b: falsyValue, c: falsyValue}};
tryParseAndIgnoreException('a.b.c = 1');
tryParseAndIgnoreException('a.c["d"] = 2');
expect(scope).toEqual({a: {b: falsyValue, c: falsyValue}});
// Helpers
//
// Normally assigning property on a primitive should throw exception in strict mode
// and silently fail in non-strict mode, IE seems to always have the non-strict-mode behavior,
// so if we try to use 'expect(function() {$parse('a.b=1')({a:false});).toThrow()' for testing
// the test will fail in case of IE because it will not throw exception, and if we just use
// '$parse('a.b=1')({a:false})' the test will fail because it will throw exception in case of Chrome
// so we use tryParseAndIgnoreException helper to catch the exception silently for all cases.
//
function tryParseAndIgnoreException(expression) {
try {
$parse(expression)(scope);
} catch (error) {/* ignore exception */}
}
});
});
});
describe('literal', function() {