| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library logical_rewriter; | |
| 6 | |
| 7 import '../constants/values.dart' as values; | |
| 8 import 'tree_ir_nodes.dart'; | |
| 9 | |
| 10 /// Rewrites logical expressions to be more compact in the Tree IR. | |
| 11 /// | |
| 12 /// In this class an expression is said to occur in "boolean context" if | |
| 13 /// its result is immediately applied to boolean conversion. | |
| 14 /// | |
| 15 /// IF STATEMENTS: | |
| 16 /// | |
| 17 /// We apply the following two rules to [If] statements (see [visitIf]). | |
| 18 /// | |
| 19 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted) | |
| 20 /// if (!E) S1 else S2 ==> if (E) S2 else S1 (unless previous rule applied) | |
| 21 /// | |
| 22 /// NEGATION: | |
| 23 /// | |
| 24 /// De Morgan's Laws are used to rewrite negations of logical operators so | |
| 25 /// negations are closer to the root: | |
| 26 /// | |
| 27 /// !x && !y --> !(x || y) | |
| 28 /// | |
| 29 /// This is to enable other rewrites, such as branch swapping in an if. In some | |
| 30 /// contexts, the rule is reversed because we do not expect to apply a rewrite | |
| 31 /// rule to the result. For example: | |
| 32 /// | |
| 33 /// z = !(x || y) ==> z = !x && !y; | |
| 34 /// | |
| 35 /// CONDITIONALS: | |
| 36 /// | |
| 37 /// Conditionals with boolean constant operands occur frequently in the input. | |
| 38 /// They can often the re-written to logical operators, for instance: | |
| 39 /// | |
| 40 /// if (x ? y : false) S1 else S2 | |
| 41 /// ==> | |
| 42 /// if (x && y) S1 else S2 | |
| 43 /// | |
| 44 /// Conditionals are tricky to rewrite when they occur out of boolean context. | |
| 45 /// Here we must apply more conservative rules, such as: | |
| 46 /// | |
| 47 /// x ? true : false ==> !!x | |
| 48 /// | |
| 49 /// If an operand is known to be a boolean, we can introduce a logical operator: | |
| 50 /// | |
| 51 /// x ? y : false ==> x && y (if y is known to be a boolean) | |
| 52 /// | |
| 53 /// The following sequence of rewrites demonstrates the merit of these rules: | |
| 54 /// | |
| 55 /// x ? (y ? true : false) : false | |
| 56 /// x ? !!y : false (double negation introduced by [toBoolean]) | |
| 57 /// x && !!y (!!y validated by [isBooleanValued]) | |
| 58 /// x && y (double negation removed by [putInBooleanContext]) | |
| 59 /// | |
| 60 class LogicalRewriter extends Visitor<Statement, Expression> { | |
| 61 | |
| 62 /// Statement to be executed next by natural fallthrough. Although fallthrough | |
| 63 /// is not introduced in this phase, we need to reason about fallthrough when | |
| 64 /// evaluating the benefit of swapping the branches of an [If]. | |
| 65 Statement fallthrough; | |
| 66 | |
| 67 void rewrite(FunctionDefinition definition) { | |
| 68 if (definition.isAbstract) return; | |
| 69 | |
| 70 definition.body = visitStatement(definition.body); | |
| 71 } | |
| 72 | |
| 73 Statement visitLabeledStatement(LabeledStatement node) { | |
| 74 Statement savedFallthrough = fallthrough; | |
| 75 fallthrough = node.next; | |
| 76 node.body = visitStatement(node.body); | |
| 77 fallthrough = savedFallthrough; | |
| 78 node.next = visitStatement(node.next); | |
| 79 return node; | |
| 80 } | |
| 81 | |
| 82 Statement visitAssign(Assign node) { | |
| 83 node.definition = visitExpression(node.definition); | |
| 84 node.next = visitStatement(node.next); | |
| 85 return node; | |
| 86 } | |
| 87 | |
| 88 Statement visitReturn(Return node) { | |
| 89 node.value = visitExpression(node.value); | |
| 90 return node; | |
| 91 } | |
| 92 | |
| 93 Statement visitBreak(Break node) { | |
| 94 return node; | |
| 95 } | |
| 96 | |
| 97 Statement visitContinue(Continue node) { | |
| 98 return node; | |
| 99 } | |
| 100 | |
| 101 bool isFallthroughBreak(Statement node) { | |
| 102 return node is Break && node.target.binding.next == fallthrough; | |
| 103 } | |
| 104 | |
| 105 Statement visitIf(If node) { | |
| 106 // If one of the branches is empty (i.e. just a fallthrough), then that | |
| 107 // branch should preferrably be the 'else' so we won't have to print it. | |
| 108 // In other words, we wish to perform this rewrite: | |
| 109 // if (E) {} else {S} | |
| 110 // ==> | |
| 111 // if (!E) {S} | |
| 112 // In the tree language, empty statements do not exist yet, so we must check | |
| 113 // if one branch contains a break that can be eliminated by fallthrough. | |
| 114 | |
| 115 // Swap branches if then is a fallthrough break. | |
| 116 if (isFallthroughBreak(node.thenStatement)) { | |
| 117 node.condition = new Not(node.condition); | |
| 118 Statement tmp = node.thenStatement; | |
| 119 node.thenStatement = node.elseStatement; | |
| 120 node.elseStatement = tmp; | |
| 121 } | |
| 122 | |
| 123 // Can the else part be eliminated? | |
| 124 // (Either due to the above swap or if the break was already there). | |
| 125 bool emptyElse = isFallthroughBreak(node.elseStatement); | |
| 126 | |
| 127 node.condition = makeCondition(node.condition, true, liftNots: !emptyElse); | |
| 128 node.thenStatement = visitStatement(node.thenStatement); | |
| 129 node.elseStatement = visitStatement(node.elseStatement); | |
| 130 | |
| 131 // If neither branch is empty, eliminate a negation in the condition | |
| 132 // if (!E) S1 else S2 | |
| 133 // ==> | |
| 134 // if (E) S2 else S1 | |
| 135 if (!emptyElse && node.condition is Not) { | |
| 136 node.condition = (node.condition as Not).operand; | |
| 137 Statement tmp = node.thenStatement; | |
| 138 node.thenStatement = node.elseStatement; | |
| 139 node.elseStatement = tmp; | |
| 140 } | |
| 141 | |
| 142 return node; | |
| 143 } | |
| 144 | |
| 145 Statement visitWhileTrue(WhileTrue node) { | |
| 146 node.body = visitStatement(node.body); | |
| 147 return node; | |
| 148 } | |
| 149 | |
| 150 Statement visitWhileCondition(WhileCondition node) { | |
| 151 node.condition = makeCondition(node.condition, true, liftNots: false); | |
| 152 node.body = visitStatement(node.body); | |
| 153 node.next = visitStatement(node.next); | |
| 154 return node; | |
| 155 } | |
| 156 | |
| 157 Statement visitExpressionStatement(ExpressionStatement node) { | |
| 158 node.expression = visitExpression(node.expression); | |
| 159 node.next = visitStatement(node.next); | |
| 160 return node; | |
| 161 } | |
| 162 | |
| 163 | |
| 164 Expression visitVariable(Variable node) { | |
| 165 return node; | |
| 166 } | |
| 167 | |
| 168 Expression visitInvokeStatic(InvokeStatic node) { | |
| 169 _rewriteList(node.arguments); | |
| 170 return node; | |
| 171 } | |
| 172 | |
| 173 Expression visitInvokeMethod(InvokeMethod node) { | |
| 174 node.receiver = visitExpression(node.receiver); | |
| 175 _rewriteList(node.arguments); | |
| 176 return node; | |
| 177 } | |
| 178 | |
| 179 Expression visitInvokeSuperMethod(InvokeSuperMethod node) { | |
| 180 _rewriteList(node.arguments); | |
| 181 return node; | |
| 182 } | |
| 183 | |
| 184 Expression visitInvokeConstructor(InvokeConstructor node) { | |
| 185 _rewriteList(node.arguments); | |
| 186 return node; | |
| 187 } | |
| 188 | |
| 189 Expression visitConcatenateStrings(ConcatenateStrings node) { | |
| 190 _rewriteList(node.arguments); | |
| 191 return node; | |
| 192 } | |
| 193 | |
| 194 Expression visitLiteralList(LiteralList node) { | |
| 195 _rewriteList(node.values); | |
| 196 return node; | |
| 197 } | |
| 198 | |
| 199 Expression visitLiteralMap(LiteralMap node) { | |
| 200 _rewriteList(node.keys); | |
| 201 _rewriteList(node.values); | |
| 202 return node; | |
| 203 } | |
| 204 | |
| 205 Expression visitTypeOperator(TypeOperator node) { | |
| 206 node.receiver = visitExpression(node.receiver); | |
| 207 return node; | |
| 208 } | |
| 209 | |
| 210 Expression visitConstant(Constant node) { | |
| 211 return node; | |
| 212 } | |
| 213 | |
| 214 Expression visitThis(This node) { | |
| 215 return node; | |
| 216 } | |
| 217 | |
| 218 Expression visitReifyTypeVar(ReifyTypeVar node) { | |
| 219 return node; | |
| 220 } | |
| 221 | |
| 222 Expression visitFunctionExpression(FunctionExpression node) { | |
| 223 new LogicalRewriter().rewrite(node.definition); | |
| 224 return node; | |
| 225 } | |
| 226 | |
| 227 Statement visitFunctionDeclaration(FunctionDeclaration node) { | |
| 228 new LogicalRewriter().rewrite(node.definition); | |
| 229 node.next = visitStatement(node.next); | |
| 230 return node; | |
| 231 } | |
| 232 | |
| 233 Expression visitNot(Not node) { | |
| 234 return toBoolean(makeCondition(node.operand, false, liftNots: false)); | |
| 235 } | |
| 236 | |
| 237 Expression visitConditional(Conditional node) { | |
| 238 // node.condition will be visited after the then and else parts, because its | |
| 239 // polarity depends on what rewrite we use. | |
| 240 node.thenExpression = visitExpression(node.thenExpression); | |
| 241 node.elseExpression = visitExpression(node.elseExpression); | |
| 242 | |
| 243 // In the following, we must take care not to eliminate or introduce a | |
| 244 // boolean conversion. | |
| 245 | |
| 246 // x ? true : false --> !!x | |
| 247 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) { | |
| 248 return toBoolean(makeCondition(node.condition, true, liftNots: false)); | |
| 249 } | |
| 250 // x ? false : true --> !x | |
| 251 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) { | |
| 252 return toBoolean(makeCondition(node.condition, false, liftNots: false)); | |
| 253 } | |
| 254 | |
| 255 // x ? y : false ==> x && y (if y is known to be a boolean) | |
| 256 if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) { | |
| 257 return new LogicalOperator.and( | |
| 258 makeCondition(node.condition, true, liftNots:false), | |
| 259 putInBooleanContext(node.thenExpression)); | |
| 260 } | |
| 261 // x ? y : true ==> !x || y (if y is known to be a boolean) | |
| 262 if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) { | |
| 263 return new LogicalOperator.or( | |
| 264 makeCondition(node.condition, false, liftNots: false), | |
| 265 putInBooleanContext(node.thenExpression)); | |
| 266 } | |
| 267 // x ? true : y ==> x || y (if y if known to be boolean) | |
| 268 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) { | |
| 269 return new LogicalOperator.or( | |
| 270 makeCondition(node.condition, true, liftNots: false), | |
| 271 putInBooleanContext(node.elseExpression)); | |
| 272 } | |
| 273 // x ? false : y ==> !x && y (if y is known to be a boolean) | |
| 274 if (isBooleanValued(node.elseExpression) && isFalse(node.thenExpression)) { | |
| 275 return new LogicalOperator.and( | |
| 276 makeCondition(node.condition, false, liftNots: false), | |
| 277 putInBooleanContext(node.elseExpression)); | |
| 278 } | |
| 279 | |
| 280 node.condition = makeCondition(node.condition, true); | |
| 281 | |
| 282 // !x ? y : z ==> x ? z : y | |
| 283 if (node.condition is Not) { | |
| 284 node.condition = (node.condition as Not).operand; | |
| 285 Expression tmp = node.thenExpression; | |
| 286 node.thenExpression = node.elseExpression; | |
| 287 node.elseExpression = tmp; | |
| 288 } | |
| 289 | |
| 290 return node; | |
| 291 } | |
| 292 | |
| 293 Expression visitLogicalOperator(LogicalOperator node) { | |
| 294 node.left = makeCondition(node.left, true); | |
| 295 node.right = makeCondition(node.right, true); | |
| 296 return node; | |
| 297 } | |
| 298 | |
| 299 /// True if the given expression is known to evaluate to a boolean. | |
| 300 /// This will not recursively traverse [Conditional] expressions, but if | |
| 301 /// applied to the result of [visitExpression] conditionals will have been | |
| 302 /// rewritten anyway. | |
| 303 bool isBooleanValued(Expression e) { | |
| 304 return isTrue(e) || isFalse(e) || e is Not || e is LogicalOperator; | |
| 305 } | |
| 306 | |
| 307 /// Rewrite an expression that was originally processed in a non-boolean | |
| 308 /// context. | |
| 309 Expression putInBooleanContext(Expression e) { | |
| 310 if (e is Not && e.operand is Not) { | |
| 311 return (e.operand as Not).operand; | |
| 312 } else { | |
| 313 return e; | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 /// Forces a boolean conversion of the given expression. | |
| 318 Expression toBoolean(Expression e) { | |
| 319 if (isBooleanValued(e)) | |
| 320 return e; | |
| 321 else | |
| 322 return new Not(new Not(e)); | |
| 323 } | |
| 324 | |
| 325 /// Creates an equivalent boolean expression. The expression must occur in a | |
| 326 /// context where its result is immediately subject to boolean conversion. | |
| 327 /// If [polarity] if false, the negated condition will be created instead. | |
| 328 /// If [liftNots] is true (default) then Not expressions will be lifted toward | |
| 329 /// the root the condition so they can be eliminated by the caller. | |
| 330 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) { | |
| 331 if (e is Not) { | |
| 332 // !!E ==> E | |
| 333 return makeCondition(e.operand, !polarity, liftNots: liftNots); | |
| 334 } | |
| 335 if (e is LogicalOperator) { | |
| 336 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y | |
| 337 e.left = makeCondition(e.left, polarity); | |
| 338 e.right = makeCondition(e.right, polarity); | |
| 339 if (!polarity) { | |
| 340 e.isAnd = !e.isAnd; | |
| 341 } | |
| 342 // !x && !y ==> !(x || y) (only if lifting nots) | |
| 343 if (e.left is Not && e.right is Not && liftNots) { | |
| 344 e.left = (e.left as Not).operand; | |
| 345 e.right = (e.right as Not).operand; | |
| 346 e.isAnd = !e.isAnd; | |
| 347 return new Not(e); | |
| 348 } | |
| 349 return e; | |
| 350 } | |
| 351 if (e is Conditional) { | |
| 352 // Handle polarity by: !(x ? y : z) ==> x ? !y : !z | |
| 353 // Rewrite individual branches now. The condition will be rewritten | |
| 354 // when we know what polarity to use (depends on which rewrite is used). | |
| 355 e.thenExpression = makeCondition(e.thenExpression, polarity); | |
| 356 e.elseExpression = makeCondition(e.elseExpression, polarity); | |
| 357 | |
| 358 // x ? true : false ==> x | |
| 359 if (isTrue(e.thenExpression) && isFalse(e.elseExpression)) { | |
| 360 return makeCondition(e.condition, true, liftNots: liftNots); | |
| 361 } | |
| 362 // x ? false : true ==> !x | |
| 363 if (isFalse(e.thenExpression) && isTrue(e.elseExpression)) { | |
| 364 return makeCondition(e.condition, false, liftNots: liftNots); | |
| 365 } | |
| 366 // x ? true : y ==> x || y | |
| 367 if (isTrue(e.thenExpression)) { | |
| 368 return makeOr(makeCondition(e.condition, true), | |
| 369 e.elseExpression, | |
| 370 liftNots: liftNots); | |
| 371 } | |
| 372 // x ? false : y ==> !x && y | |
| 373 if (isFalse(e.thenExpression)) { | |
| 374 return makeAnd(makeCondition(e.condition, false), | |
| 375 e.elseExpression, | |
| 376 liftNots: liftNots); | |
| 377 } | |
| 378 // x ? y : true ==> !x || y | |
| 379 if (isTrue(e.elseExpression)) { | |
| 380 return makeOr(makeCondition(e.condition, false), | |
| 381 e.thenExpression, | |
| 382 liftNots: liftNots); | |
| 383 } | |
| 384 // x ? y : false ==> x && y | |
| 385 if (isFalse(e.elseExpression)) { | |
| 386 return makeAnd(makeCondition(e.condition, true), | |
| 387 e.thenExpression, | |
| 388 liftNots: liftNots); | |
| 389 } | |
| 390 | |
| 391 e.condition = makeCondition(e.condition, true); | |
| 392 | |
| 393 // !x ? y : z ==> x ? z : y | |
| 394 if (e.condition is Not) { | |
| 395 e.condition = (e.condition as Not).operand; | |
| 396 Expression tmp = e.thenExpression; | |
| 397 e.thenExpression = e.elseExpression; | |
| 398 e.elseExpression = tmp; | |
| 399 } | |
| 400 // x ? !y : !z ==> !(x ? y : z) (only if lifting nots) | |
| 401 if (e.thenExpression is Not && e.elseExpression is Not && liftNots) { | |
| 402 e.thenExpression = (e.thenExpression as Not).operand; | |
| 403 e.elseExpression = (e.elseExpression as Not).operand; | |
| 404 return new Not(e); | |
| 405 } | |
| 406 return e; | |
| 407 } | |
| 408 if (e is Constant && e.value.isBool) { | |
| 409 // !true ==> false | |
| 410 if (!polarity) { | |
| 411 values.BoolConstantValue value = e.value; | |
| 412 return new Constant.primitive(value.negate()); | |
| 413 } | |
| 414 return e; | |
| 415 } | |
| 416 e = visitExpression(e); | |
| 417 return polarity ? e : new Not(e); | |
| 418 } | |
| 419 | |
| 420 bool isTrue(Expression e) { | |
| 421 return e is Constant && e.value.isTrue; | |
| 422 } | |
| 423 | |
| 424 bool isFalse(Expression e) { | |
| 425 return e is Constant && e.value.isFalse; | |
| 426 } | |
| 427 | |
| 428 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) { | |
| 429 if (e1 is Not && e2 is Not && liftNots) { | |
| 430 return new Not(new LogicalOperator.or(e1.operand, e2.operand)); | |
| 431 } else { | |
| 432 return new LogicalOperator.and(e1, e2); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) { | |
| 437 if (e1 is Not && e2 is Not && liftNots) { | |
| 438 return new Not(new LogicalOperator.and(e1.operand, e2.operand)); | |
| 439 } else { | |
| 440 return new LogicalOperator.or(e1, e2); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 /// Destructively updates each entry of [l] with the result of visiting it. | |
| 445 void _rewriteList(List<Expression> l) { | |
| 446 for (int i = 0; i < l.length; i++) { | |
| 447 l[i] = visitExpression(l[i]); | |
| 448 } | |
| 449 } | |
| 450 } | |
| 451 | |
| OLD | NEW |