Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 4f44e01

Browse files
m-amrlgalfaso
authored andcommitted
fix($parse): treat falsy values as defined in assignment expressions
Closes #14990 Closes #14994
1 parent 606ea5d commit 4f44e01

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

src/ng/parse.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ ASTCompiler.prototype = {
944944
self.if_(self.stage === 'inputs' || 's', function() {
945945
if (create && create !== 1) {
946946
self.if_(
947-
self.not(self.nonComputedMember('s', ast.name)),
947+
self.isNull(self.nonComputedMember('s', ast.name)),
948948
self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
949949
}
950950
self.assign(intoId, self.nonComputedMember('s', ast.name));
@@ -973,7 +973,7 @@ ASTCompiler.prototype = {
973973
}
974974
} else {
975975
if (create && create !== 1) {
976-
self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
976+
self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
977977
}
978978
expression = self.nonComputedMember(left, ast.property.name);
979979
self.assign(intoId, expression);
@@ -1155,6 +1155,10 @@ ASTCompiler.prototype = {
11551155
return '!(' + expression + ')';
11561156
},
11571157

1158+
isNull: function(expression) {
1159+
return expression + '==null';
1160+
},
1161+
11581162
notNull: function(expression) {
11591163
return expression + '!=null';
11601164
},
@@ -1546,7 +1550,7 @@ ASTInterpreter.prototype = {
15461550
identifier: function(name, context, create, expression) {
15471551
return function(scope, locals, assign, inputs) {
15481552
var base = locals && (name in locals) ? locals : scope;
1549-
if (create && create !== 1 && base && !(base[name])) {
1553+
if (create && create !== 1 && base && base[name] == null) {
15501554
base[name] = {};
15511555
}
15521556
var value = base ? base[name] : undefined;
@@ -1583,7 +1587,7 @@ ASTInterpreter.prototype = {
15831587
return function(scope, locals, assign, inputs) {
15841588
var lhs = left(scope, locals, assign, inputs);
15851589
if (create && create !== 1) {
1586-
if (lhs && !(lhs[right])) {
1590+
if (lhs && lhs[right] == null) {
15871591
lhs[right] = {};
15881592
}
15891593
}

test/ng/parseSpec.js

+66
Original file line numberDiff line numberDiff line change
@@ -3155,6 +3155,72 @@ describe('parser', function() {
31553155
expect(isFunction(s.toString)).toBe(true);
31563156
expect(l.toString).toBe(1);
31573157
}));
3158+
3159+
it('should overwrite undefined / null scope properties when assigning', inject(function($parse) {
3160+
var scope;
3161+
3162+
scope = {};
3163+
$parse('a.b = 1')(scope);
3164+
$parse('c["d"] = 2')(scope);
3165+
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
3166+
3167+
scope = {a: {}};
3168+
$parse('a.b.c = 1')(scope);
3169+
$parse('a.c["d"] = 2')(scope);
3170+
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
3171+
3172+
scope = {a: undefined, c: undefined};
3173+
$parse('a.b = 1')(scope);
3174+
$parse('c["d"] = 2')(scope);
3175+
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
3176+
3177+
scope = {a: {b: undefined, c: undefined}};
3178+
$parse('a.b.c = 1')(scope);
3179+
$parse('a.c["d"] = 2')(scope);
3180+
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
3181+
3182+
scope = {a: null, c: null};
3183+
$parse('a.b = 1')(scope);
3184+
$parse('c["d"] = 2')(scope);
3185+
expect(scope).toEqual({a: {b: 1}, c: {d: 2}});
3186+
3187+
scope = {a: {b: null, c: null}};
3188+
$parse('a.b.c = 1')(scope);
3189+
$parse('a.c["d"] = 2')(scope);
3190+
expect(scope).toEqual({a: {b: {c: 1}, c: {d: 2}}});
3191+
}));
3192+
3193+
they('should not overwrite $prop scope properties when assigning', [0, false, '', NaN],
3194+
function(falsyValue) {
3195+
inject(function($parse) {
3196+
var scope;
3197+
3198+
scope = {a: falsyValue, c: falsyValue};
3199+
tryParseAndIgnoreException('a.b = 1');
3200+
tryParseAndIgnoreException('c["d"] = 2');
3201+
expect(scope).toEqual({a: falsyValue, c: falsyValue});
3202+
3203+
scope = {a: {b: falsyValue, c: falsyValue}};
3204+
tryParseAndIgnoreException('a.b.c = 1');
3205+
tryParseAndIgnoreException('a.c["d"] = 2');
3206+
expect(scope).toEqual({a: {b: falsyValue, c: falsyValue}});
3207+
3208+
// Helpers
3209+
//
3210+
// Normally assigning property on a primitive should throw exception in strict mode
3211+
// and silently fail in non-strict mode, IE seems to always have the non-strict-mode behavior,
3212+
// so if we try to use 'expect(function() {$parse('a.b=1')({a:false});).toThrow()' for testing
3213+
// the test will fail in case of IE because it will not throw exception, and if we just use
3214+
// '$parse('a.b=1')({a:false})' the test will fail because it will throw exception in case of Chrome
3215+
// so we use tryParseAndIgnoreException helper to catch the exception silently for all cases.
3216+
//
3217+
function tryParseAndIgnoreException(expression) {
3218+
try {
3219+
$parse(expression)(scope);
3220+
} catch (error) {/* ignore exception */}
3221+
}
3222+
});
3223+
});
31583224
});
31593225

31603226
describe('literal', function() {

0 commit comments

Comments
 (0)