Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (C) 2012 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (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 | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 /** | |
16 * @fileoverview Mitigate deviations between SES and ES5-strict code | |
17 * by rewriting programs where possible. The output of this stage is | |
18 * outside the TCB. | |
MarkM
2012/11/12 18:16:08
Please put in a "see" link to http://code.google.c
Jasvir
2012/11/19 14:39:55
Done.
| |
19 * | |
20 * Note the parse tree manipulate in this file uses the SpiderMonkey | |
metaweta
2012/11/12 19:16:24
That sentence doesn't parse.
Jasvir
2012/11/19 14:39:55
Done.
| |
21 * standard AST | |
22 * | |
23 * @author Jasvir Nagra (jasvir@google.com) | |
24 * @requires estraverse, acorn, escodegen | |
25 * @overrides ses | |
26 | |
27 */ | |
28 | |
29 (function() { | |
30 function introducesScope(node) { | |
31 return node.type === 'FunctionExpression' || | |
32 node.type === 'FunctionDeclaration'; | |
33 } | |
34 ·· | |
35 function isTypeOf(node) { | |
36 return (node.type === 'UnaryExpression' && | |
37 node.operator === 'typeof' && | |
38 !node.synthetic); | |
39 } | |
40 ·· | |
41 function isVariableDecl(node) { | |
42 return (node.type === 'VariableDeclaration'); | |
43 } | |
44 ·· | |
45 /** | |
46 * Rewrite var decls in place into assignments on the global object | |
47 * turning expression "var x, y = 2, z" to | |
48 * window.x = window.x, window.y = 2, window.z = window.z | |
metaweta
2012/11/12 19:16:24
Under what conditions do we throw ReferenceErrors
Jasvir
2012/11/19 14:39:55
Like strict mode, if a variable is used on the RHS
| |
49 */ | |
50 function rewriteVars(node) { | |
51 var assignments = []; | |
52 node.declarations.forEach(function(decl) { | |
53 assignments.push({ | |
54 "type": "AssignmentExpression", | |
55 "operator": "=", | |
56 "left": globalVarAst(decl.id), | |
57 "right": decl.init || globalVarAst(decl.id) | |
58 }); | |
59 ·· | |
60 }); | |
61 node.type = "ExpressionStatement"; | |
62 node.expression = { | |
63 "type": "SequenceExpression", | |
64 "expressions": assignments | |
65 }; | |
66 } | |
67 ·· | |
68 function globalVarAst(varName) { | |
69 return { | |
70 "type": "MemberExpression", | |
71 "object": { | |
72 "type": "Identifier", | |
73 "name": "window" | |
74 }, | |
75 "property": varName | |
76 }; | |
77 } | |
78 ·· | |
79 /** | |
80 * Rewrite node in place turning expression "typeof x" to | |
81 * (function() { try { typeof x } catch (e) { return "undefined" } })() | |
metaweta
2012/11/12 19:16:24
typeof (function(){throw Error()})(); // Should be
Jasvir
2012/11/19 14:39:55
Yeah - I'll document this as a gotcha for now -- t
| |
82 */ | |
83 function rewriteTypeOf(node) { | |
84 var arg = node.argument; | |
85 node.type = "CallExpression"; | |
86 node.arguments = []; | |
87 node.callee = { | |
88 "type": "FunctionExpression", | |
89 "id": null, | |
90 "params": [], | |
91 "body": { | |
92 "type": "BlockStatement", | |
93 "body": [{ | |
94 "type": "TryStatement", | |
95 "block": { | |
96 "type": "BlockStatement", | |
97 "body": [{ | |
98 "type": "ReturnStatement", | |
99 "argument": { | |
100 "synthetic": true, | |
101 "type": "UnaryExpression", | |
102 "operator": "typeof", | |
103 "prefix": true, | |
104 "argument": arg | |
105 } | |
106 } | |
107 ] | |
108 }, | |
109 "handlers": [{ | |
110 "type": "CatchClause", | |
111 "param": { | |
112 "type": "Identifier", | |
113 "name": "e" | |
114 }, | |
115 "guard": null, | |
116 "body": { | |
117 "type": "BlockStatement", | |
118 "body": [{ | |
119 "type": "ReturnStatement", | |
120 "argument": { | |
121 "type": "Literal", | |
122 "value": "undefined", | |
123 "raw": "'undefined'" | |
124 } | |
125 }] | |
126 } | |
127 } | |
128 ], | |
129 "finalizer": null | |
130 } | |
131 ] | |
132 } | |
133 }; | |
134 } | |
135 ·· | |
136 function resolveOptions(options) { | |
137 function resolve(opt, defaultOption) { | |
138 return (options && opt in options) ? opt : defaultOption; | |
139 } | |
140 var resolved = {}; | |
141 resolved.rewriteTopLevelVars = resolve('rewriteTopLevelVars', true); | |
142 resolved.rewriteTypeOf = resolve('rewriteTypeOf', true); | |
143 return resolved; | |
144 } | |
145 | |
146 ses.mitigateGotchas = function(programSrc, options) { | |
147 options = resolveOptions(options); | |
148 var scopeLevel = 0; | |
149 var ast = acorn.parse(programSrc); | |
150 estraverse.traverse(ast, { | |
151 enter: function enter(node) { | |
152 if (introducesScope(node)) { | |
153 scopeLevel++; | |
154 } else if (options.rewriteTypeOf && isTypeOf(node)) { | |
155 rewriteTypeOf(node); | |
156 } else if (options.rewriteTopLevelVars && | |
157 isVariableDecl(node) && scopeLevel === 0) { | |
158 rewriteVars(node); | |
159 } | |
160 }, | |
161 leave: function leave(node) { | |
162 if (introducesScope(node)) { | |
163 scopeLevel--; | |
164 } | |
165 } | |
166 }); | |
167 return escodegen.generate(ast); | |
168 }; | |
169 | |
170 })(); | |
OLD | NEW |