| 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 node.entries.forEach((LiteralMapEntry entry) { | |
| 201 entry.key = visitExpression(entry.key); | |
| 202 entry.value = visitExpression(entry.value); | |
| 203 }); | |
| 204 return node; | |
| 205 } | |
| 206 | |
| 207 Expression visitTypeOperator(TypeOperator node) { | |
| 208 node.receiver = visitExpression(node.receiver); | |
| 209 return node; | |
| 210 } | |
| 211 | |
| 212 Expression visitConstant(Constant node) { | |
| 213 return node; | |
| 214 } | |
| 215 | |
| 216 Expression visitThis(This node) { | |
| 217 return node; | |
| 218 } | |
| 219 | |
| 220 Expression visitReifyTypeVar(ReifyTypeVar node) { | |
| 221 return node; | |
| 222 } | |
| 223 | |
| 224 Expression visitFunctionExpression(FunctionExpression node) { | |
| 225 new LogicalRewriter().rewrite(node.definition); | |
| 226 return node; | |
| 227 } | |
| 228 | |
| 229 Statement visitFunctionDeclaration(FunctionDeclaration node) { | |
| 230 new LogicalRewriter().rewrite(node.definition); | |
| 231 node.next = visitStatement(node.next); | |
| 232 return node; | |
| 233 } | |
| 234 | |
| 235 Expression visitNot(Not node) { | |
| 236 return toBoolean(makeCondition(node.operand, false, liftNots: false)); | |
| 237 } | |
| 238 | |
| 239 Expression visitConditional(Conditional node) { | |
| 240 // node.condition will be visited after the then and else parts, because its | |
| 241 // polarity depends on what rewrite we use. | |
| 242 node.thenExpression = visitExpression(node.thenExpression); | |
| 243 node.elseExpression = visitExpression(node.elseExpression); | |
| 244 | |
| 245 // In the following, we must take care not to eliminate or introduce a | |
| 246 // boolean conversion. | |
| 247 | |
| 248 // x ? true : false --> !!x | |
| 249 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) { | |
| 250 return toBoolean(makeCondition(node.condition, true, liftNots: false)); | |
| 251 } | |
| 252 // x ? false : true --> !x | |
| 253 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) { | |
| 254 return toBoolean(makeCondition(node.condition, false, liftNots: false)); | |
| 255 } | |
| 256 | |
| 257 // x ? y : false ==> x && y (if y is known to be a boolean) | |
| 258 if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) { | |
| 259 return new LogicalOperator.and( | |
| 260 makeCondition(node.condition, true, liftNots:false), | |
| 261 putInBooleanContext(node.thenExpression)); | |
| 262 } | |
| 263 // x ? y : true ==> !x || y (if y is known to be a boolean) | |
| 264 if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) { | |
| 265 return new LogicalOperator.or( | |
| 266 makeCondition(node.condition, false, liftNots: false), | |
| 267 putInBooleanContext(node.thenExpression)); | |
| 268 } | |
| 269 // x ? true : y ==> x || y (if y if known to be boolean) | |
| 270 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) { | |
| 271 return new LogicalOperator.or( | |
| 272 makeCondition(node.condition, true, liftNots: false), | |
| 273 putInBooleanContext(node.elseExpression)); | |
| 274 } | |
| 275 // x ? false : y ==> !x && y (if y is known to be a boolean) | |
| 276 if (isBooleanValued(node.elseExpression) && isFalse(node.thenExpression)) { | |
| 277 return new LogicalOperator.and( | |
| 278 makeCondition(node.condition, false, liftNots: false), | |
| 279 putInBooleanContext(node.elseExpression)); | |
| 280 } | |
| 281 | |
| 282 node.condition = makeCondition(node.condition, true); | |
| 283 | |
| 284 // !x ? y : z ==> x ? z : y | |
| 285 if (node.condition is Not) { | |
| 286 node.condition = (node.condition as Not).operand; | |
| 287 Expression tmp = node.thenExpression; | |
| 288 node.thenExpression = node.elseExpression; | |
| 289 node.elseExpression = tmp; | |
| 290 } | |
| 291 | |
| 292 return node; | |
| 293 } | |
| 294 | |
| 295 Expression visitLogicalOperator(LogicalOperator node) { | |
| 296 node.left = makeCondition(node.left, true); | |
| 297 node.right = makeCondition(node.right, true); | |
| 298 return node; | |
| 299 } | |
| 300 | |
| 301 /// True if the given expression is known to evaluate to a boolean. | |
| 302 /// This will not recursively traverse [Conditional] expressions, but if | |
| 303 /// applied to the result of [visitExpression] conditionals will have been | |
| 304 /// rewritten anyway. | |
| 305 bool isBooleanValued(Expression e) { | |
| 306 return isTrue(e) || isFalse(e) || e is Not || e is LogicalOperator; | |
| 307 } | |
| 308 | |
| 309 /// Rewrite an expression that was originally processed in a non-boolean | |
| 310 /// context. | |
| 311 Expression putInBooleanContext(Expression e) { | |
| 312 if (e is Not && e.operand is Not) { | |
| 313 return (e.operand as Not).operand; | |
| 314 } else { | |
| 315 return e; | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 /// Forces a boolean conversion of the given expression. | |
| 320 Expression toBoolean(Expression e) { | |
| 321 if (isBooleanValued(e)) | |
| 322 return e; | |
| 323 else | |
| 324 return new Not(new Not(e)); | |
| 325 } | |
| 326 | |
| 327 /// Creates an equivalent boolean expression. The expression must occur in a | |
| 328 /// context where its result is immediately subject to boolean conversion. | |
| 329 /// If [polarity] if false, the negated condition will be created instead. | |
| 330 /// If [liftNots] is true (default) then Not expressions will be lifted toward | |
| 331 /// the root the condition so they can be eliminated by the caller. | |
| 332 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) { | |
| 333 if (e is Not) { | |
| 334 // !!E ==> E | |
| 335 return makeCondition(e.operand, !polarity, liftNots: liftNots); | |
| 336 } | |
| 337 if (e is LogicalOperator) { | |
| 338 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y | |
| 339 e.left = makeCondition(e.left, polarity); | |
| 340 e.right = makeCondition(e.right, polarity); | |
| 341 if (!polarity) { | |
| 342 e.isAnd = !e.isAnd; | |
| 343 } | |
| 344 // !x && !y ==> !(x || y) (only if lifting nots) | |
| 345 if (e.left is Not && e.right is Not && liftNots) { | |
| 346 e.left = (e.left as Not).operand; | |
| 347 e.right = (e.right as Not).operand; | |
| 348 e.isAnd = !e.isAnd; | |
| 349 return new Not(e); | |
| 350 } | |
| 351 return e; | |
| 352 } | |
| 353 if (e is Conditional) { | |
| 354 // Handle polarity by: !(x ? y : z) ==> x ? !y : !z | |
| 355 // Rewrite individual branches now. The condition will be rewritten | |
| 356 // when we know what polarity to use (depends on which rewrite is used). | |
| 357 e.thenExpression = makeCondition(e.thenExpression, polarity); | |
| 358 e.elseExpression = makeCondition(e.elseExpression, polarity); | |
| 359 | |
| 360 // x ? true : false ==> x | |
| 361 if (isTrue(e.thenExpression) && isFalse(e.elseExpression)) { | |
| 362 return makeCondition(e.condition, true, liftNots: liftNots); | |
| 363 } | |
| 364 // x ? false : true ==> !x | |
| 365 if (isFalse(e.thenExpression) && isTrue(e.elseExpression)) { | |
| 366 return makeCondition(e.condition, false, liftNots: liftNots); | |
| 367 } | |
| 368 // x ? true : y ==> x || y | |
| 369 if (isTrue(e.thenExpression)) { | |
| 370 return makeOr(makeCondition(e.condition, true), | |
| 371 e.elseExpression, | |
| 372 liftNots: liftNots); | |
| 373 } | |
| 374 // x ? false : y ==> !x && y | |
| 375 if (isFalse(e.thenExpression)) { | |
| 376 return makeAnd(makeCondition(e.condition, false), | |
| 377 e.elseExpression, | |
| 378 liftNots: liftNots); | |
| 379 } | |
| 380 // x ? y : true ==> !x || y | |
| 381 if (isTrue(e.elseExpression)) { | |
| 382 return makeOr(makeCondition(e.condition, false), | |
| 383 e.thenExpression, | |
| 384 liftNots: liftNots); | |
| 385 } | |
| 386 // x ? y : false ==> x && y | |
| 387 if (isFalse(e.elseExpression)) { | |
| 388 return makeAnd(makeCondition(e.condition, true), | |
| 389 e.thenExpression, | |
| 390 liftNots: liftNots); | |
| 391 } | |
| 392 | |
| 393 e.condition = makeCondition(e.condition, true); | |
| 394 | |
| 395 // !x ? y : z ==> x ? z : y | |
| 396 if (e.condition is Not) { | |
| 397 e.condition = (e.condition as Not).operand; | |
| 398 Expression tmp = e.thenExpression; | |
| 399 e.thenExpression = e.elseExpression; | |
| 400 e.elseExpression = tmp; | |
| 401 } | |
| 402 // x ? !y : !z ==> !(x ? y : z) (only if lifting nots) | |
| 403 if (e.thenExpression is Not && e.elseExpression is Not && liftNots) { | |
| 404 e.thenExpression = (e.thenExpression as Not).operand; | |
| 405 e.elseExpression = (e.elseExpression as Not).operand; | |
| 406 return new Not(e); | |
| 407 } | |
| 408 return e; | |
| 409 } | |
| 410 if (e is Constant && e.value.isBool) { | |
| 411 // !true ==> false | |
| 412 if (!polarity) { | |
| 413 values.BoolConstantValue value = e.value; | |
| 414 return new Constant.primitive(value.negate()); | |
| 415 } | |
| 416 return e; | |
| 417 } | |
| 418 e = visitExpression(e); | |
| 419 return polarity ? e : new Not(e); | |
| 420 } | |
| 421 | |
| 422 bool isTrue(Expression e) { | |
| 423 return e is Constant && e.value.isTrue; | |
| 424 } | |
| 425 | |
| 426 bool isFalse(Expression e) { | |
| 427 return e is Constant && e.value.isFalse; | |
| 428 } | |
| 429 | |
| 430 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) { | |
| 431 if (e1 is Not && e2 is Not && liftNots) { | |
| 432 return new Not(new LogicalOperator.or(e1.operand, e2.operand)); | |
| 433 } else { | |
| 434 return new LogicalOperator.and(e1, e2); | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) { | |
| 439 if (e1 is Not && e2 is Not && liftNots) { | |
| 440 return new Not(new LogicalOperator.and(e1.operand, e2.operand)); | |
| 441 } else { | |
| 442 return new LogicalOperator.or(e1, e2); | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 /// Destructively updates each entry of [l] with the result of visiting it. | |
| 447 void _rewriteList(List<Expression> l) { | |
| 448 for (int i = 0; i < l.length; i++) { | |
| 449 l[i] = visitExpression(l[i]); | |
| 450 } | |
| 451 } | |
| 452 } | |
| 453 | |
| OLD | NEW |