LEFT | RIGHT |
1 // Copyright (C) 2011 Google Inc. | 1 // Copyright (C) 2011 Google Inc. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 * Given the sourceText for a strict Program, | 177 * Given the sourceText for a strict Program, |
178 * atLeastFreeVarNames(sourceText) returns a Record whose | 178 * atLeastFreeVarNames(sourceText) returns a Record whose |
179 * enumerable own property names must include the names of all the | 179 * enumerable own property names must include the names of all the |
180 * free variables occuring in sourceText. It can include as | 180 * free variables occuring in sourceText. It can include as |
181 * many other strings as is convenient so long as it includes | 181 * many other strings as is convenient so long as it includes |
182 * these. The value of each of these properties should be | 182 * these. The value of each of these properties should be |
183 * {@code true}. TODO(erights): On platforms with Proxies | 183 * {@code true}. TODO(erights): On platforms with Proxies |
184 * (currently only Firefox 4 and after), use {@code | 184 * (currently only Firefox 4 and after), use {@code |
185 * with(aProxy) {...}} to intercept free variables rather than | 185 * with(aProxy) {...}} to intercept free variables rather than |
186 * atLeastFreeVarNames. | 186 * atLeastFreeVarNames. |
187 * @param mitigateGotchas ::F([string], Record(any)) | 187 * @param opt_mitigateGotchas ::F([string], Record(any)) | undefined |
188 * Given the sourceText for a strict Program, | 188 * Given the sourceText for a strict Program, if provided, |
189 * mitigateGotchas(sourceText, options) returns rewritten | 189 * opt_mitigateGotchas(sourceText, options) returns rewritten |
190 * program with the same semantics as the original but with as | 190 * program with the same semantics as the original but with as |
191 * many of the gotchas removed as possible. {@code options} is | 191 * many of the gotchas removed as possible. {@code options} is |
192 * a record of which gotcha-rewriting-stages to use or omit. | 192 * a record of which gotcha-rewriting-stages to use or omit. |
193 * Passing no option performs the default. | 193 * Passing no option performs the default. |
194 * @param extensions ::F([], Record(any)]) A function returning a | 194 * @param extensions ::F([], Record(any)]) A function returning a |
195 * record whose own properties will be copied onto cajaVM. This | 195 * record whose own properties will be copied onto cajaVM. This |
196 * is used for the optional components which bring SES to | 196 * is used for the optional components which bring SES to |
197 * feature parity with the ES5/3 runtime at the price of larger | 197 * feature parity with the ES5/3 runtime at the price of larger |
198 * code size. At the time that {@code startSES} calls {@code | 198 * code size. At the time that {@code startSES} calls {@code |
199 * extensions}, {@code cajaVM} exists but should not yet be | 199 * extensions}, {@code cajaVM} exists but should not yet be |
200 * used. In particular, {@code extensions} should not call | 200 * used. In particular, {@code extensions} should not call |
201 * {@code cajaVM.def} during this setup, because def would then | 201 * {@code cajaVM.def} during this setup, because def would then |
202 * freeze priordials before startSES cleans them (removes | 202 * freeze priordials before startSES cleans them (removes |
203 * non-whitelisted properties). The methods that | 203 * non-whitelisted properties). The methods that |
204 * {@code extensions} contributes can, of course, use | 204 * {@code extensions} contributes can, of course, use |
205 * {@code cajaVM}, since those methods will only be called once | 205 * {@code cajaVM}, since those methods will only be called once |
206 * {@code startSES} finishes. | 206 * {@code startSES} finishes. |
207 */ | 207 */ |
208 ses.startSES = function(global, | 208 ses.startSES = function(global, |
209 whitelist, | 209 whitelist, |
210 atLeastFreeVarNames, | 210 atLeastFreeVarNames, |
211 mitigateGotchas, | 211 opt_mitigateGotchas, |
212 extensions) { | 212 extensions) { |
213 "use strict"; | 213 "use strict"; |
214 | 214 |
215 /////////////// KLUDGE SWITCHES /////////////// | 215 /////////////// KLUDGE SWITCHES /////////////// |
216 | 216 |
217 ///////////////////////////////// | 217 ///////////////////////////////// |
218 // The following are only the minimal kludges needed for the current | 218 // The following are only the minimal kludges needed for the current |
219 // Firefox or the current Chrome Beta. At the time of | 219 // Firefox or the current Chrome Beta. At the time of |
220 // this writing, these are Firefox 4.0 and Chrome 12.0.742.5 dev | 220 // this writing, these are Firefox 4.0 and Chrome 12.0.742.5 dev |
221 // As these move forward, kludges can be removed until we simply | 221 // As these move forward, kludges can be removed until we simply |
(...skipping 28 matching lines...) Expand all Loading... |
250 | 250 |
251 var hop = Object.prototype.hasOwnProperty; | 251 var hop = Object.prototype.hasOwnProperty; |
252 | 252 |
253 var getProto = Object.getPrototypeOf; | 253 var getProto = Object.getPrototypeOf; |
254 var defProp = Object.defineProperty; | 254 var defProp = Object.defineProperty; |
255 var gopd = Object.getOwnPropertyDescriptor; | 255 var gopd = Object.getOwnPropertyDescriptor; |
256 var gopn = Object.getOwnPropertyNames; | 256 var gopn = Object.getOwnPropertyNames; |
257 var keys = Object.keys; | 257 var keys = Object.keys; |
258 var freeze = Object.freeze; | 258 var freeze = Object.freeze; |
259 var create = Object.create; | 259 var create = Object.create; |
| 260 var mitigateGotchas = opt_mitigateGotchas || function (s) { return '' + s; }; |
260 | 261 |
261 /** | 262 /** |
262 * Use to tamper proof a function which is not intended to ever be | 263 * Use to tamper proof a function which is not intended to ever be |
263 * used as a constructor, since it nulls out the function's | 264 * used as a constructor, since it nulls out the function's |
264 * prototype first. | 265 * prototype first. |
265 */ | 266 */ |
266 function constFunc(func) { | 267 function constFunc(func) { |
267 func.prototype = null; | 268 func.prototype = null; |
268 return freeze(func); | 269 return freeze(func); |
269 } | 270 } |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
533 } | 534 } |
534 // if it were possible to know that the getter call was on | 535 // if it were possible to know that the getter call was on |
535 // behalf of a typeof expression, we'd return the string | 536 // behalf of a typeof expression, we'd return the string |
536 // "undefined" here instead. Unfortunately, without | 537 // "undefined" here instead. Unfortunately, without |
537 // parsing or proxies, that isn't possible. | 538 // parsing or proxies, that isn't possible. |
538 throw new ReferenceError('"' + name + '" blocked by Caja'); | 539 throw new ReferenceError('"' + name + '" blocked by Caja'); |
539 }, | 540 }, |
540 set: function scopedSet(newValue) { | 541 set: function scopedSet(newValue) { |
541 if (name in imports) { | 542 if (name in imports) { |
542 imports[name] = newValue; | 543 imports[name] = newValue; |
543 return newValue; | 544 return; |
544 } | 545 } |
545 throw new TypeError('Cannot set "' + name + '"'); | 546 throw new TypeError('Cannot set "' + name + '"'); |
546 }, | 547 }, |
547 enumerable: false | 548 enumerable: false |
548 }; | 549 }; |
549 } | 550 } |
550 desc.enumerable = false; | 551 desc.enumerable = false; |
551 defProp(scopeObject, name, desc); | 552 defProp(scopeObject, name, desc); |
552 }); | 553 }); |
553 return freeze(scopeObject); | 554 return freeze(scopeObject); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
634 * Program and return a function that evaluates it to the | 635 * Program and return a function that evaluates it to the |
635 * Program's completion value. Unfortunately, this is not | 636 * Program's completion value. Unfortunately, this is not |
636 * practical as a library without some non-standard support from | 637 * practical as a library without some non-standard support from |
637 * the platform such as a parser API that provides an AST. | 638 * the platform such as a parser API that provides an AST. |
638 * TODO(jasvir): Now that we're parsing, we can provide compileProgram. | 639 * TODO(jasvir): Now that we're parsing, we can provide compileProgram. |
639 * | 640 * |
640 * <p>Thanks to Mike Samuel and Ankur Taly for this trick of using | 641 * <p>Thanks to Mike Samuel and Ankur Taly for this trick of using |
641 * {@code with} together with RegExp matching to intercept free | 642 * {@code with} together with RegExp matching to intercept free |
642 * variable access without parsing. | 643 * variable access without parsing. |
643 */ | 644 */ |
644 function compileExpr(exprSrc, opt_sourcePosition) { | 645 function compileExpr(src, opt_sourcePosition) { |
| 646 // Force src to be parsed as an expr |
| 647 var exprSrc = '(' + src + '\n)'; |
645 exprSrc = mitigateGotchas(exprSrc); | 648 exprSrc = mitigateGotchas(exprSrc); |
| 649 // This is a workaround for a bug in the escodegen renderer that |
| 650 // renders expressions as expression statements |
646 if (exprSrc[exprSrc.length - 1] === ';') { | 651 if (exprSrc[exprSrc.length - 1] === ';') { |
647 exprSrc = exprSrc.substr(0, exprSrc.length - 1); | 652 exprSrc = exprSrc.substr(0, exprSrc.length - 1); |
648 } | 653 } |
649 var wrapperSrc = securableWrapperSrc(exprSrc, opt_sourcePosition); | 654 var wrapperSrc = securableWrapperSrc(exprSrc, opt_sourcePosition); |
650 var wrapper = unsafeEval(wrapperSrc); | 655 var wrapper = unsafeEval(wrapperSrc); |
651 var freeNames = atLeastFreeVarNames(exprSrc); | 656 var freeNames = atLeastFreeVarNames(exprSrc); |
652 var result = makeCompiledExpr(wrapper, freeNames); | 657 var result = makeCompiledExpr(wrapper, freeNames); |
653 return freeze(result); | 658 return freeze(result); |
654 } | 659 } |
655 | 660 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
706 * | 711 * |
707 * <p>With a similarly lightweight RegExp, we should be able to | 712 * <p>With a similarly lightweight RegExp, we should be able to |
708 * similarly recognize the {@code "load"} syntax of <a href= | 713 * similarly recognize the {@code "load"} syntax of <a href= |
709 * "http://wiki.ecmascript.org/doku.php?id=strawman:simple_modules#syntax" | 714 * "http://wiki.ecmascript.org/doku.php?id=strawman:simple_modules#syntax" |
710 * >Sam and Dave's module proposal for ES-Harmony</a>. However, | 715 * >Sam and Dave's module proposal for ES-Harmony</a>. However, |
711 * since browsers do not currently accept this syntax, | 716 * since browsers do not currently accept this syntax, |
712 * {@code getRequirements} above would also have to extract these | 717 * {@code getRequirements} above would also have to extract these |
713 * from the text to be compiled. | 718 * from the text to be compiled. |
714 */ | 719 */ |
715 function compileModule(modSrc, opt_sourcePosition) { | 720 function compileModule(modSrc, opt_sourcePosition) { |
716 var exprSrc = '(function() {' + mitigateGotchas(modSrc) + '}).call(this)'; | 721 // Note the EOL after modSrc to prevent trailing line comment in modSrc |
| 722 // eliding the rest of the wrapper. |
| 723 var exprSrc = |
| 724 '(function() {' + mitigateGotchas(modSrc) + '\n}).call(this)'; |
717 | 725 |
718 // Follow the pattern in compileExpr | 726 // Follow the pattern in compileExpr |
719 var wrapperSrc = securableWrapperSrc(exprSrc, opt_sourcePosition); | 727 var wrapperSrc = securableWrapperSrc(exprSrc, opt_sourcePosition); |
720 var wrapper = unsafeEval(wrapperSrc); | 728 var wrapper = unsafeEval(wrapperSrc); |
721 var freeNames = atLeastFreeVarNames(exprSrc); | 729 var freeNames = atLeastFreeVarNames(exprSrc); |
722 var moduleMaker = makeCompiledExpr(wrapper, freeNames); | 730 var moduleMaker = makeCompiledExpr(wrapper, freeNames); |
723 | 731 |
724 moduleMaker.requirements = getRequirements(modSrc); | 732 moduleMaker.requirements = getRequirements(modSrc); |
725 return freeze(moduleMaker); | 733 return freeze(moduleMaker); |
726 } | 734 } |
727 | 735 |
728 /** | 736 /** |
729 * A safe form of the {@code Function} constructor, which | 737 * A safe form of the {@code Function} constructor, which |
730 * constructs strict functions that can only refer freely to the | 738 * constructs strict functions that can only refer freely to the |
731 * {@code sharedImports}. | 739 * {@code sharedImports}. |
732 * | 740 * |
733 * <p>The returned function is strict whether or not it declares | 741 * <p>The returned function is strict whether or not it declares |
734 * itself to be. | 742 * itself to be. |
735 */ | 743 */ |
736 function FakeFunction(var_args) { | 744 function FakeFunction(var_args) { |
737 var params = [].slice.call(arguments, 0); | 745 var params = [].slice.call(arguments, 0); |
738 var body = params.pop(); | 746 var body = params.pop(); |
739 body = String(body || ''); | 747 body = String(body || ''); |
740 params = params.join(','); | 748 params = params.join(','); |
741 var exprSrc = '(function(' + params + '\n){' + body + '})'; | 749 // Note the EOL after modSrc to prevent trailing line comment in body |
| 750 // eliding the rest of the wrapper. |
| 751 var exprSrc = '(function(' + params + '\n){' + body + '\n})'; |
742 return compileExpr(exprSrc)(sharedImports); | 752 return compileExpr(exprSrc)(sharedImports); |
743 } | 753 } |
744 FakeFunction.prototype = UnsafeFunction.prototype; | 754 FakeFunction.prototype = UnsafeFunction.prototype; |
745 FakeFunction.prototype.constructor = FakeFunction; | 755 FakeFunction.prototype.constructor = FakeFunction; |
746 global.Function = FakeFunction; | 756 global.Function = FakeFunction; |
747 | 757 |
748 /** | 758 /** |
749 * A safe form of the indirect {@code eval} function, which | 759 * A safe form of the indirect {@code eval} function, which |
750 * evaluates {@code src} as strict code that can only refer freely | 760 * evaluates {@code src} as strict code that can only refer freely |
751 * to the {@code sharedImports}. | 761 * to the {@code sharedImports}. |
(...skipping 570 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1322 ses.logger.reportMax(); | 1332 ses.logger.reportMax(); |
1323 | 1333 |
1324 if (ses.ok(ses['severities'][ses.maxAcceptableSeverityName])) { | 1334 if (ses.ok(ses['severities'][ses.maxAcceptableSeverityName])) { |
1325 // We succeeded. Enable safe Function, eval, and compile* to work. | 1335 // We succeeded. Enable safe Function, eval, and compile* to work. |
1326 dirty = false; | 1336 dirty = false; |
1327 ses.logger.log('initSES succeeded.'); | 1337 ses.logger.log('initSES succeeded.'); |
1328 } else { | 1338 } else { |
1329 ses.logger.error('initSES failed.'); | 1339 ses.logger.error('initSES failed.'); |
1330 } | 1340 } |
1331 }; | 1341 }; |
LEFT | RIGHT |