8000 improve expression/path parser to handle dynamic set expressions (fix… · cdxfish/vue@ac616f6 · GitHub
[go: up one dir, main page]

Skip to content

Commit ac616f6

Browse files
committed
improve expression/path parser to handle dynamic set expressions (fix vuejs#1797)
1 parent 673a70f commit ac616f6

File tree

7 files changed

+128
-233
lines changed

7 files changed

+128
-233
lines changed

src/compiler/compile-props.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var propBindingModes = require('../config')._propBindingModes
55
var empty = {}
66

77
// regexes
8-
var identRE = require('../parsers/path').identRE
8+
var identRE = /^[$_a-zA-Z]+[\w$]*$/
99
var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/
1010

1111
/**

src/parsers/expression.js

Lines changed: 24 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,10 @@ function restore (str, i) {
100100
* `scope.` and generate getter/setter functions.
101101
*
102102
* @param {String} exp
103-
* @param {Boolean} needSet
104103
* @return {Function}
105104
*/
106105

107-
function compileExpFns (exp, needSet) {
106+
function compileGetter (exp) {
108107
if (improperKeywordsRE.test(exp)) {
109108
process.env.NODE_ENV !== 'production' && _.warn(
110109
'Avoid using reserved keywords in expression: ' + exp
@@ -121,44 +120,7 @@ function compileExpFns (exp, needSet) {
121120
body = (' ' + body)
122121
.replace(pathReplaceRE, rewrite)
123122
.replace(restoreRE, restore)
124-
var getter = makeGetter(body)
125-
if (getter) {
126-
return {
127-
get: getter,
128-
body: body,
129-
set: needSet
130-
? makeSetter(body)
131-
: null
132-
}
133-
}
134-
}
135-
136-
/**
137-
* Compile getter setters for a simple path.
138-
*
139-
* @param {String} exp
140-
* @return {Function}
141-
*/
142-
143-
function compilePathFns (exp) {
144-
var getter, path
145-
if (exp.indexOf('[') < 0) {
146-
// really simple path
147-
path = exp.split('.')
148-
path.raw = exp
149-
getter = Path.compileGetter(path)
150-
} else {
151-
// do the real parsing
152-
path = Path.parse(exp)
153-
getter = path.get
154-
}
155-
return {
156-
get: getter,
157-
// always generate setter for simple paths
158-
set: function (obj, val) {
159-
Path.set(obj, path, val)
160-
}
161-
}
123+
return makeGetterFn(body)
162124
}
163125

164126
/**
@@ -171,7 +133,7 @@ function compilePathFns (exp) {
171133
* @return {Function|undefined}
172134
*/
173135

174-
function makeGetter (body) {
136+
function makeGetterFn (body) {
175137
try {
176138
return new Function('scope', 'return ' + body + ';')
177139
} catch (e) {
@@ -183,41 +145,25 @@ function makeGetter (body) {
183145
}
184146

185147
/**
186-
* Build a setter function.
187-
*
188-
* This is only needed in rare situations like "a[b]" where
189-
* a settable path requires dynamic evaluation.
148+
* Compile a setter function for the expression.
190149
*
191-
* This setter function may throw error when called if the
192-
* expression body is not a valid left-hand expression in
193-
* assignment.
194-
*
195-
* @param {String} body
150+
* @param {String} exp
196151
* @return {Function|undefined}
197152
*/
198153

199-
function makeSetter (body) {
200-
try {
201-
return new Function('scope', 'value', body + '=value;')
202-
} catch (e) {
154+
function compileSetter (exp) {
155+
var path = Path.parse(exp)
156+
if (path) {
157+
return function (scope, val) {
158+
Path.set(scope, path, val)
159+
}
160+
} else {
203161
process.env.NODE_ENV !== 'production' && _.warn(
204-
'Invalid setter function body: ' + body
162+
'Invalid setter expression: ' + exp
205163
)
206164
}
207165
}
208166

209-
/**
210-
* Check for setter existence on a cache hit.
211-
*
212-
* @param {Function} hit
213-
*/
214-
215-
function checkSetter (hit) {
216-
if (!hit.set) {
217-
hit.set = makeSetter(hit.body)
218-
}
219-
}
220-
221167
/**
222168
* Parse an expression into re-written getter/setters.
223169
*
@@ -231,19 +177,20 @@ exports.parse = function (exp, needSet) {
231177
// try cache
232178
var hit = expressionCache.get(exp)
233179
if (hit) {
234-
if (needSet) {
235-
checkSetter(hit)
180+
if (needSet && !hit.set) {
181+
hit.set = compileSetter(hit.exp)
236182
}
237183
return hit
238184
}
239-
// we do a simple path check to optimize for them.
240-
// the check fails valid paths with unusal whitespaces,
241-
// but that's too rare and we don't care.
242-
// also skip boolean literals and paths that start with
243-
// global "Math"
244-
var res = exports.isSimplePath(exp)
245-
? compilePathFns(exp)
246-
: compileExpFns(exp, needSet)
185+
var res = { exp: exp }
186+
res.get = exports.isSimplePath(exp) && exp.indexOf('[') < 0
187+
// optimized super simple getter
188+
? makeGetterFn('scope.' + exp)
189+
// dynamic getter
190+
: compileGetter(exp)
191+
if (needSet) {
192+
res.set = compileSetter(exp)
193+
}
247194
expressionCache.put(exp, res)
248195
return res
249196
}

0 commit comments

Comments
 (0)
0