github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/themes/wind/static/libs/vue-1.0.24/src/parsers/expression.js (about) 1 import { warn } from '../util/index' 2 import { parsePath, setPath } from './path' 3 import Cache from '../cache' 4 5 const expressionCache = new Cache(1000) 6 7 const allowedKeywords = 8 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 9 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 10 'encodeURIComponent,parseInt,parseFloat' 11 const allowedKeywordsRE = 12 new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)') 13 14 // keywords that don't make sense inside expressions 15 const improperKeywords = 16 'break,case,class,catch,const,continue,debugger,default,' + 17 'delete,do,else,export,extends,finally,for,function,if,' + 18 'import,in,instanceof,let,return,super,switch,throw,try,' + 19 'var,while,with,yield,enum,await,implements,package,' + 20 'protected,static,interface,private,public' 21 const improperKeywordsRE = 22 new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)') 23 24 const wsRE = /\s/g 25 const newlineRE = /\n/g 26 const saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g 27 const restoreRE = /"(\d+)"/g 28 const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/ 29 const identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g 30 const booleanLiteralRE = /^(?:true|false)$/ 31 32 /** 33 * Save / Rewrite / Restore 34 * 35 * When rewriting paths found in an expression, it is 36 * possible for the same letter sequences to be found in 37 * strings and Object literal property keys. Therefore we 38 * remove and store these parts in a temporary array, and 39 * restore them after the path rewrite. 40 */ 41 42 var saved = [] 43 44 /** 45 * Save replacer 46 * 47 * The save regex can match two possible cases: 48 * 1. An opening object literal 49 * 2. A string 50 * If matched as a plain string, we need to escape its 51 * newlines, since the string needs to be preserved when 52 * generating the function body. 53 * 54 * @param {String} str 55 * @param {String} isString - str if matched as a string 56 * @return {String} - placeholder with index 57 */ 58 59 function save (str, isString) { 60 var i = saved.length 61 saved[i] = isString 62 ? str.replace(newlineRE, '\\n') 63 : str 64 return '"' + i + '"' 65 } 66 67 /** 68 * Path rewrite replacer 69 * 70 * @param {String} raw 71 * @return {String} 72 */ 73 74 function rewrite (raw) { 75 var c = raw.charAt(0) 76 var path = raw.slice(1) 77 if (allowedKeywordsRE.test(path)) { 78 return raw 79 } else { 80 path = path.indexOf('"') > -1 81 ? path.replace(restoreRE, restore) 82 : path 83 return c + 'scope.' + path 84 } 85 } 86 87 /** 88 * Restore replacer 89 * 90 * @param {String} str 91 * @param {String} i - matched save index 92 * @return {String} 93 */ 94 95 function restore (str, i) { 96 return saved[i] 97 } 98 99 /** 100 * Rewrite an expression, prefixing all path accessors with 101 * `scope.` and generate getter/setter functions. 102 * 103 * @param {String} exp 104 * @return {Function} 105 */ 106 107 function compileGetter (exp) { 108 if (improperKeywordsRE.test(exp)) { 109 process.env.NODE_ENV !== 'production' && warn( 110 'Avoid using reserved keywords in expression: ' + exp 111 ) 112 } 113 // reset state 114 saved.length = 0 115 // save strings and object literal keys 116 var body = exp 117 .replace(saveRE, save) 118 .replace(wsRE, '') 119 // rewrite all paths 120 // pad 1 space here becaue the regex matches 1 extra char 121 body = (' ' + body) 122 .replace(identRE, rewrite) 123 .replace(restoreRE, restore) 124 return makeGetterFn(body) 125 } 126 127 /** 128 * Build a getter function. Requires eval. 129 * 130 * We isolate the try/catch so it doesn't affect the 131 * optimization of the parse function when it is not called. 132 * 133 * @param {String} body 134 * @return {Function|undefined} 135 */ 136 137 function makeGetterFn (body) { 138 try { 139 /* eslint-disable no-new-func */ 140 return new Function('scope', 'return ' + body + ';') 141 /* eslint-enable no-new-func */ 142 } catch (e) { 143 process.env.NODE_ENV !== 'production' && warn( 144 'Invalid expression. ' + 145 'Generated function body: ' + body 146 ) 147 } 148 } 149 150 /** 151 * Compile a setter function for the expression. 152 * 153 * @param {String} exp 154 * @return {Function|undefined} 155 */ 156 157 function compileSetter (exp) { 158 var path = parsePath(exp) 159 if (path) { 160 return function (scope, val) { 161 setPath(scope, path, val) 162 } 163 } else { 164 process.env.NODE_ENV !== 'production' && warn( 165 'Invalid setter expression: ' + exp 166 ) 167 } 168 } 169 170 /** 171 * Parse an expression into re-written getter/setters. 172 * 173 * @param {String} exp 174 * @param {Boolean} needSet 175 * @return {Function} 176 */ 177 178 export function parseExpression (exp, needSet) { 179 exp = exp.trim() 180 // try cache 181 var hit = expressionCache.get(exp) 182 if (hit) { 183 if (needSet && !hit.set) { 184 hit.set = compileSetter(hit.exp) 185 } 186 return hit 187 } 188 var res = { exp: exp } 189 res.get = isSimplePath(exp) && exp.indexOf('[') < 0 190 // optimized super simple getter 191 ? makeGetterFn('scope.' + exp) 192 // dynamic getter 193 : compileGetter(exp) 194 if (needSet) { 195 res.set = compileSetter(exp) 196 } 197 expressionCache.put(exp, res) 198 return res 199 } 200 201 /** 202 * Check if an expression is a simple path. 203 * 204 * @param {String} exp 205 * @return {Boolean} 206 */ 207 208 export function isSimplePath (exp) { 209 return pathTestRE.test(exp) && 210 // don't treat true/false as paths 211 !booleanLiteralRE.test(exp) && 212 // Math constants e.g. Math.PI, Math.E etc. 213 exp.slice(0, 5) !== 'Math.' 214 }