Skip to content
Permalink
Browse files

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

Closes #14990
Closes #14994
  • Loading branch information
m-amr authored and lgalfaso committed Sep 11, 2016
1 parent 606ea5d commit 4f44e018948c45bfb07f0170de4f703d22778d71
Showing with 74 additions and 4 deletions.
  1. +8 −4 src/ng/parse.js
  2. +66 −0 test/ng/parseSpec.js
@@ -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] = {};
}
}
@@ -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() {

0 comments on commit 4f44e01

Please sign in to comment.
You can’t perform that action at this time.