OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of native; | 5 part of native; |
6 | 6 |
7 class SideEffectsVisitor extends js.BaseVisitor { | 7 class SideEffectsVisitor extends js.BaseVisitor { |
8 final SideEffects sideEffects; | 8 final SideEffects sideEffects; |
9 SideEffectsVisitor(this.sideEffects); | 9 SideEffectsVisitor(this.sideEffects); |
10 | 10 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 node.visitChildren(this); | 74 node.visitChildren(this); |
75 } | 75 } |
76 | 76 |
77 void visitAccess(js.PropertyAccess node) { | 77 void visitAccess(js.PropertyAccess node) { |
78 sideEffects.setDependsOnIndexStore(); | 78 sideEffects.setDependsOnIndexStore(); |
79 sideEffects.setDependsOnInstancePropertyStore(); | 79 sideEffects.setDependsOnInstancePropertyStore(); |
80 sideEffects.setDependsOnStaticPropertyStore(); | 80 sideEffects.setDependsOnStaticPropertyStore(); |
81 node.visitChildren(this); | 81 node.visitChildren(this); |
82 } | 82 } |
83 } | 83 } |
| 84 |
| 85 |
| 86 /// ThrowBehaviorVisitor generates a NativeThrowBehavior describing the |
| 87 /// exception behavior of a JavaScript expression. |
| 88 /// |
| 89 /// The result is semi-conservative, giving reasonable results for many simple |
| 90 /// JS fragments. The non-conservative part is the assumption that binary |
| 91 /// operators are used on 'good' operands that do not force arbirary code to be |
| 92 /// executed via conversions (valueOf() and toString() methods). |
| 93 /// |
| 94 /// In many cases a JS fragment has more precise behavior. In these cases the |
| 95 /// behavior should be described as a property of the JS fragment. For example, |
| 96 /// Object.keys(#) has a TypeError on null / undefined, which can only be known |
| 97 /// in the calling context. |
| 98 /// |
| 99 class ThrowBehaviorVisitor extends js.BaseVisitor<NativeThrowBehavior> { |
| 100 |
| 101 ThrowBehaviorVisitor(); |
| 102 |
| 103 NativeThrowBehavior analyze(js.Node node) { |
| 104 return visit(node); |
| 105 } |
| 106 |
| 107 // TODO(sra): Add [sequence] functionality to NativeThrowBehavior. |
| 108 /// Returns the combined behavior of sequential execution of code having |
| 109 /// behavior [first] followed by code having behavior [second]. |
| 110 static NativeThrowBehavior sequence(NativeThrowBehavior first, |
| 111 NativeThrowBehavior second) { |
| 112 if (first == NativeThrowBehavior.MUST) return first; |
| 113 if (second == NativeThrowBehavior.MUST) return second; |
| 114 if (second == NativeThrowBehavior.NEVER) return first; |
| 115 if (first == NativeThrowBehavior.NEVER) return second; |
| 116 // Both are one of MAY or MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS. |
| 117 return NativeThrowBehavior.MAY; |
| 118 } |
| 119 |
| 120 // TODO(sra): Add [choice] functionality to NativeThrowBehavior. |
| 121 /// Returns the combined behavior of a choice between two paths with behaviors |
| 122 /// [first] and [second]. |
| 123 static NativeThrowBehavior choice(NativeThrowBehavior first, |
| 124 NativeThrowBehavior second) { |
| 125 if (first == second) return first; // Both paths have same behaviour. |
| 126 return NativeThrowBehavior.MAY; |
| 127 } |
| 128 |
| 129 NativeThrowBehavior visit(js.Node node) { |
| 130 return node.accept(this); |
| 131 } |
| 132 |
| 133 NativeThrowBehavior visitNode(js.Node node) { |
| 134 return NativeThrowBehavior.MAY; |
| 135 } |
| 136 |
| 137 NativeThrowBehavior visitLiteral(js.Literal node) { |
| 138 return NativeThrowBehavior.NEVER; |
| 139 } |
| 140 |
| 141 NativeThrowBehavior visitInterpolatedExpression(js.InterpolatedNode node) { |
| 142 return NativeThrowBehavior.NEVER; |
| 143 } |
| 144 |
| 145 NativeThrowBehavior visitInterpolatedSelector(js.InterpolatedNode node) { |
| 146 return NativeThrowBehavior.NEVER; |
| 147 } |
| 148 |
| 149 NativeThrowBehavior visitObjectInitializer(js.ObjectInitializer node) { |
| 150 NativeThrowBehavior result = NativeThrowBehavior.NEVER; |
| 151 for (js.Property property in node.properties) { |
| 152 result = sequence(result, visit(property)); |
| 153 } |
| 154 return result; |
| 155 } |
| 156 |
| 157 NativeThrowBehavior visitProperty(js.Property node) { |
| 158 return sequence(visit(node.name), visit(node.value)); |
| 159 } |
| 160 |
| 161 NativeThrowBehavior visitAssignment(js.Assignment node) { |
| 162 // TODO(sra): Can we make "#.p = #" be null(1)? |
| 163 return NativeThrowBehavior.MAY; |
| 164 } |
| 165 |
| 166 NativeThrowBehavior visitCall(js.Call node) { |
| 167 return NativeThrowBehavior.MAY; |
| 168 } |
| 169 |
| 170 NativeThrowBehavior visitNew(js.New node) { |
| 171 // TODO(sra): `new Array(x)` where `x` is a small number. |
| 172 return NativeThrowBehavior.MAY; |
| 173 } |
| 174 |
| 175 NativeThrowBehavior visitBinary(js.Binary node) { |
| 176 NativeThrowBehavior left = visit(node.left); |
| 177 NativeThrowBehavior right = visit(node.right); |
| 178 switch (node.op) { |
| 179 // We make the non-conservative assumption that these operations are not |
| 180 // used in ways that force calling arbitrary code via valueOf or |
| 181 // toString(). |
| 182 case "*": |
| 183 case "/": |
| 184 case "%": |
| 185 case "+": |
| 186 case "-": |
| 187 case "<<": |
| 188 case ">>": |
| 189 case ">>>": |
| 190 case "<": |
| 191 case ">": |
| 192 case "<=": |
| 193 case ">=": |
| 194 case "==": |
| 195 case "===": |
| 196 case "!=": |
| 197 case "!==": |
| 198 case "&": |
| 199 case "^": |
| 200 case "|": |
| 201 return sequence(left, right); |
| 202 |
| 203 case ',': |
| 204 return sequence(left, right); |
| 205 |
| 206 case "&&": |
| 207 case "||": |
| 208 return choice(left, sequence(left, right)); |
| 209 |
| 210 case "instanceof": |
| 211 case "in": |
| 212 default: |
| 213 return NativeThrowBehavior.MAY; |
| 214 } |
| 215 } |
| 216 |
| 217 NativeThrowBehavior visitThrow(js.Throw node) { |
| 218 return NativeThrowBehavior.MUST; |
| 219 } |
| 220 |
| 221 NativeThrowBehavior visitPrefix(js.Prefix node) { |
| 222 if (node.op == 'typeof' && node.argument is js.VariableUse) |
| 223 return NativeThrowBehavior.NEVER; |
| 224 NativeThrowBehavior result = visit(node.argument); |
| 225 switch (node.op) { |
| 226 case '!': |
| 227 case '~': |
| 228 case 'void': |
| 229 case 'typeof': |
| 230 return result; |
| 231 default: |
| 232 return NativeThrowBehavior.MAY; |
| 233 } |
| 234 } |
| 235 |
| 236 NativeThrowBehavior visitVariableUse(js.VariableUse node) { |
| 237 // We could get a ReferenceError unless the variable is in scope. The AST |
| 238 // could distinguish in-scope and out-of scope references. For JS fragments, |
| 239 // the only use of VariableUse should be for global references. Certain |
| 240 // global names are almost certainly not reference errors, e.g 'Array'. |
| 241 switch (node.name) { |
| 242 case 'Array': |
| 243 case 'Object': |
| 244 return NativeThrowBehavior.NEVER; |
| 245 default: |
| 246 return NativeThrowBehavior.MAY; |
| 247 } |
| 248 } |
| 249 |
| 250 NativeThrowBehavior visitAccess(js.PropertyAccess node) { |
| 251 // TODO(sra): We need a representation where the nsm guard behaviour is |
| 252 // maintained when combined with other throwing behaviour. |
| 253 js.Node receiver = node.receiver; |
| 254 NativeThrowBehavior first = visit(receiver); |
| 255 NativeThrowBehavior second = visit(node.selector); |
| 256 |
| 257 if (receiver is js.InterpolatedExpression && |
| 258 receiver.isPositional && |
| 259 receiver.nameOrPosition == 0) { |
| 260 first = NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS; |
| 261 } else { |
| 262 first = NativeThrowBehavior.MAY; |
| 263 } |
| 264 |
| 265 return sequence(first, second); |
| 266 } |
| 267 } |
OLD | NEW |