From 1e7873afc0518b841482f9639d9c6cc14c4a1ace Mon Sep 17 00:00:00 2001 From: harttle Date: Sun, 6 Nov 2016 22:08:26 +0800 Subject: [PATCH] fix scope hidding --- src/scope.js | 60 +++++++++++++++++++++++++++------------------------ test/scope.js | 24 ++++++++++++++------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/scope.js b/src/scope.js index 3da6354dd..72e808ce0 100644 --- a/src/scope.js +++ b/src/scope.js @@ -1,33 +1,35 @@ const _ = require('./util/underscore.js'); const lexical = require('./lexical.js'); const assert = require('./util/assert.js'); +const referenceError = /undefined variable|Cannot read property .* of undefined/; var Scope = { - safeGet: function(str) { - var i; - // get all - if (str === undefined) { - var ctx = {}; - for (i = this.scopes.length - 1; i >= 0; i--) { - var scp = this.scopes[i]; - for (var k in scp) { - if (scp.hasOwnProperty(k)) { - ctx[k] = scp[k]; - } - } - } - return ctx; - } - // get one path + getAll: function(str) { + var ctx = {}; for (i = this.scopes.length - 1; i >= 0; i--) { - var v = this.getPropertyByPath(this.scopes[i], str); - if (v !== undefined) return v; + _.assign(ctx, this.scopes[i]); } + return ctx; }, get: function(str) { - var val = this.safeGet(str); - if (val === undefined && this.opts.strict_variables) { - throw new Error(`[strict_variables] undefined variable: ${str}`); + for (var i = this.scopes.length - 1; i >= 0; i--) { + try { + return this.getPropertyByPath(this.scopes[i], str); + } catch (e) { + if (!referenceError.test(e.message) || this.opts.strict_variables) { + e.message += ': ' + str; + throw e; + } + } + } + if(this.opts.strict_variables){ + throw new TypeError('undefined variable: ' + str); + } + }, + safeGet: function(str) { + try { + var val = this.safeGet(str); + } catch (e) {; } return val; }, @@ -65,12 +67,14 @@ var Scope = { }, getPropertyByPath: function(obj, path) { - if (_.isString(path) && path.length) { - var paths = this.propertyAccessSeq(path); - paths.forEach(p => obj = obj && obj[p]); - return obj; + var paths = this.propertyAccessSeq(path + ''); + var varName = paths.shift(); + if (!obj.hasOwnProperty(varName)) { + throw new TypeError('undefined variable'); } - return obj[path]; + var variable = obj[varName]; + paths.forEach(p => variable = variable[p]); + return variable; }, /* @@ -96,11 +100,11 @@ var Scope = { assert(j !== -1, `unbalanced []: ${str}`); name = str.slice(i + 1, j); // foo[1] - if(lexical.isInteger(name)){ + if (lexical.isInteger(name)) { seq.push(name); } // foo["bar"] - else{ + else { seq.push(this.get(name)); } name = ''; diff --git a/test/scope.js b/test/scope.js index 83913d7db..430abc69f 100644 --- a/test/scope.js +++ b/test/scope.js @@ -83,7 +83,7 @@ describe('scope', function() { }); it('should get all properties when arguments empty', function() { - expect(scope.get()).deep.equal(ctx); + expect(scope.getAll()).deep.equal(ctx); }); it('should access child property via dot syntax', function() { @@ -116,7 +116,7 @@ describe('scope', function() { }); }); - describe('.push(), .pop()', function() { + describe('.push()', function() { it('should throw when trying to push non-object', function() { expect(function() { scope.push(false); @@ -130,7 +130,14 @@ describe('scope', function() { expect(scope.get('foo')).to.equal('foo'); expect(scope.get('bar')).to.equal('bar'); }); - + it('should hide deep properties by push', function(){ + scope.set('bar', {bar: 'bar'}); + scope.push({bar: {foo: 'foo'}}); + expect(scope.get('bar.foo')).to.equal('foo'); + expect(scope.get('bar.bar')).to.equal(undefined); + }); + }); + describe('.pop()', function() { it('should pop scope', function() { scope.push({ foo: 'foo' @@ -140,7 +147,7 @@ describe('scope', function() { }); }); - describe('.push(), .pop()', function() { + describe('.unshift()', function() { it('should throw when trying to unshift non-object', function() { expect(function() { scope.unshift(false); @@ -150,11 +157,12 @@ describe('scope', function() { scope.unshift({ foo: 'blue', foo1: 'foo1' - }) - scope.get('foo').should.equal('zoo'); - scope.get('foo1').should.equal('foo1'); + }); + expect(scope.get('foo')).to.equal('zoo'); + expect(scope.get('foo1')).to.equal('foo1'); }); - + }); + describe('.shift()', function() { it('should shift scope', function() { scope.unshift({ foo: 'blue',