1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.databinding.tool.expr; 18 19 import android.databinding.tool.BindingTarget; 20 import android.databinding.tool.InverseBinding; 21 import android.databinding.tool.reflection.ModelAnalyzer; 22 import android.databinding.tool.reflection.ModelClass; 23 import android.databinding.tool.reflection.ModelMethod; 24 import android.databinding.tool.store.Location; 25 import android.databinding.tool.util.L; 26 import android.databinding.tool.util.Preconditions; 27 import android.databinding.tool.writer.FlagSet; 28 29 import org.antlr.v4.runtime.ParserRuleContext; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.BitSet; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 public class ExprModel { 39 40 Map<String, Expr> mExprMap = new HashMap<String, Expr>(); 41 42 List<Expr> mBindingExpressions = new ArrayList<Expr>(); 43 44 private int mInvalidateableFieldLimit = 0; 45 46 private int mRequirementIdCount = 0; 47 48 // each arg list receives a unique id even if it is the same arguments and method. 49 private int mArgListIdCounter = 0; 50 51 private static final String TRUE_KEY_SUFFIX = "== true"; 52 private static final String FALSE_KEY_SUFFIX = "== false"; 53 54 /** 55 * Any expression can be invalidated by invalidating this flag. 56 */ 57 private BitSet mInvalidateAnyFlags; 58 private int mInvalidateAnyFlagIndex; 59 60 /** 61 * Used by code generation. Keeps the list of expressions that are waiting to be evaluated. 62 */ 63 private List<Expr> mPendingExpressions; 64 65 /** 66 * Used for converting flags into identifiers while debugging. 67 */ 68 private String[] mFlagMapping; 69 70 private BitSet mInvalidateableFlags; 71 private BitSet mConditionalFlags; 72 73 private int mFlagBucketCount;// how many buckets we use to identify flags 74 75 private List<Expr> mObservables; 76 77 private boolean mSealed = false; 78 79 private Map<String, String> mImports = new HashMap<String, String>(); 80 81 private ParserRuleContext mCurrentParserContext; 82 private Location mCurrentLocationInFile; 83 /** 84 * Adds the expression to the list of expressions and returns it. 85 * If it already exists, returns existing one. 86 * 87 * @param expr The new parsed expression 88 * @return The expression itself or another one if the same thing was parsed before 89 */ register(T expr)90 public <T extends Expr> T register(T expr) { 91 Preconditions.check(!mSealed, "Cannot add expressions to a model after it is sealed"); 92 Location location = null; 93 if (mCurrentParserContext != null) { 94 location = new Location(mCurrentParserContext); 95 location.setParentLocation(mCurrentLocationInFile); 96 } 97 T existing = (T) mExprMap.get(expr.getUniqueKey()); 98 if (existing != null) { 99 Preconditions.check(expr.getParents().isEmpty(), 100 "If an expression already exists, it should've never been added to a parent," 101 + "if thats the case, somewhere we are creating an expression w/o" 102 + "calling expression model"); 103 // tell the expr that it is being swapped so that if it was added to some other expr 104 // as a parent, those can swap their references 105 expr.onSwappedWith(existing); 106 if (location != null) { 107 existing.addLocation(location); 108 } 109 return existing; 110 } 111 mExprMap.put(expr.getUniqueKey(), expr); 112 expr.setModel(this); 113 if (location != null) { 114 expr.addLocation(location); 115 } 116 return expr; 117 } 118 setCurrentParserContext(ParserRuleContext currentParserContext)119 public void setCurrentParserContext(ParserRuleContext currentParserContext) { 120 mCurrentParserContext = currentParserContext; 121 } 122 getExprMap()123 public Map<String, Expr> getExprMap() { 124 return mExprMap; 125 } 126 size()127 public int size() { 128 return mExprMap.size(); 129 } 130 comparison(String op, Expr left, Expr right)131 public ComparisonExpr comparison(String op, Expr left, Expr right) { 132 return register(new ComparisonExpr(op, left, right)); 133 } 134 instanceOfOp(Expr expr, String type)135 public InstanceOfExpr instanceOfOp(Expr expr, String type) { 136 return register(new InstanceOfExpr(expr, type)); 137 } 138 field(Expr parent, String name)139 public FieldAccessExpr field(Expr parent, String name) { 140 return register(new FieldAccessExpr(parent, name)); 141 } 142 observableField(Expr parent, String name)143 public FieldAccessExpr observableField(Expr parent, String name) { 144 return register(new FieldAccessExpr(parent, name, true)); 145 } 146 symbol(String text, Class type)147 public SymbolExpr symbol(String text, Class type) { 148 return register(new SymbolExpr(text, type)); 149 } 150 ternary(Expr pred, Expr ifTrue, Expr ifFalse)151 public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) { 152 return register(new TernaryExpr(pred, ifTrue, ifFalse)); 153 } 154 identifier(String name)155 public IdentifierExpr identifier(String name) { 156 return register(new IdentifierExpr(name)); 157 } 158 staticIdentifier(String name)159 public StaticIdentifierExpr staticIdentifier(String name) { 160 return register(new StaticIdentifierExpr(name)); 161 } 162 builtInVariable(String name, String type, String accessCode)163 public BuiltInVariableExpr builtInVariable(String name, String type, String accessCode) { 164 return register(new BuiltInVariableExpr(name, type, accessCode)); 165 } 166 viewFieldExpr(BindingTarget bindingTarget)167 public ViewFieldExpr viewFieldExpr(BindingTarget bindingTarget) { 168 return register(new ViewFieldExpr(bindingTarget)); 169 } 170 171 /** 172 * Creates a static identifier for the given class or returns the existing one. 173 */ staticIdentifierFor(final ModelClass modelClass)174 public StaticIdentifierExpr staticIdentifierFor(final ModelClass modelClass) { 175 final String type = modelClass.getCanonicalName(); 176 // check for existing 177 for (Expr expr : mExprMap.values()) { 178 if (expr instanceof StaticIdentifierExpr) { 179 StaticIdentifierExpr id = (StaticIdentifierExpr) expr; 180 if (id.getUserDefinedType().equals(type)) { 181 return id; 182 } 183 } 184 } 185 186 // does not exist. Find a name for it. 187 int cnt = 0; 188 int dotIndex = type.lastIndexOf("."); 189 String baseName; 190 Preconditions.check(dotIndex < type.length() - 1, "Invalid type %s", type); 191 if (dotIndex == -1) { 192 baseName = type; 193 } else { 194 baseName = type.substring(dotIndex + 1); 195 } 196 while (true) { 197 String candidate = cnt == 0 ? baseName : baseName + cnt; 198 if (!mImports.containsKey(candidate)) { 199 return addImport(candidate, type, null); 200 } 201 cnt ++; 202 Preconditions.check(cnt < 100, "Failed to create an import for " + type); 203 } 204 } 205 206 public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) { 207 return register(new MethodCallExpr(target, name, args)); 208 } 209 210 public MathExpr math(Expr left, String op, Expr right) { 211 return register(new MathExpr(left, op, right)); 212 } 213 214 public TernaryExpr logical(Expr left, String op, Expr right) { 215 if ("&&".equals(op)) { 216 // left && right 217 // left ? right : false 218 return register(new TernaryExpr(left, right, symbol("false", boolean.class))); 219 } else { 220 // left || right 221 // left ? true : right 222 return register(new TernaryExpr(left, symbol("true", boolean.class), right)); 223 } 224 } 225 226 public BitShiftExpr bitshift(Expr left, String op, Expr right) { 227 return register(new BitShiftExpr(left, op, right)); 228 } 229 230 public UnaryExpr unary(String op, Expr expr) { 231 return register(new UnaryExpr(op, expr)); 232 } 233 234 public Expr group(Expr grouped) { 235 return register(new GroupExpr(grouped)); 236 } 237 238 public Expr resourceExpr(String packageName, String resourceType, String resourceName, 239 List<Expr> args) { 240 return register(new ResourceExpr(packageName, resourceType, resourceName, args)); 241 } 242 243 public Expr bracketExpr(Expr variableExpr, Expr argExpr) { 244 return register(new BracketExpr(variableExpr, argExpr)); 245 } 246 247 public Expr castExpr(String type, Expr expr) { 248 return register(new CastExpr(type, expr)); 249 } 250 251 public TwoWayListenerExpr twoWayListenerExpr(InverseBinding inverseBinding) { 252 return register(new TwoWayListenerExpr(inverseBinding)); 253 } 254 public List<Expr> getBindingExpressions() { 255 return mBindingExpressions; 256 } 257 258 public StaticIdentifierExpr addImport(String alias, String type, Location location) { 259 Preconditions.check(!mImports.containsKey(alias), 260 "%s has already been defined as %s", alias, type); 261 final StaticIdentifierExpr id = staticIdentifier(alias); 262 L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName()); 263 id.setUserDefinedType(type); 264 if (location != null) { 265 id.addLocation(location); 266 } 267 mImports.put(alias, type); 268 return id; 269 } 270 271 public Map<String, String> getImports() { 272 return mImports; 273 } 274 275 /** 276 * The actual thingy that is set on the binding target. 277 * 278 * Input must be already registered 279 */ 280 public Expr bindingExpr(Expr bindingExpr) { 281 Preconditions.check(mExprMap.containsKey(bindingExpr.getUniqueKey()), 282 "Main expression should already be registered"); 283 if (!mBindingExpressions.contains(bindingExpr)) { 284 mBindingExpressions.add(bindingExpr); 285 } 286 return bindingExpr; 287 } 288 289 public void removeExpr(Expr expr) { 290 Preconditions.check(!mSealed, "Can't modify the expression list after sealing the model."); 291 mBindingExpressions.remove(expr); 292 mExprMap.remove(expr.computeUniqueKey()); 293 } 294 295 public List<Expr> getObservables() { 296 return mObservables; 297 } 298 299 /** 300 * Give id to each expression. Will be useful if we serialize. 301 */ 302 public void seal() { 303 L.d("sealing model"); 304 List<Expr> notifiableExpressions = new ArrayList<Expr>(); 305 //ensure class analyzer. We need to know observables at this point 306 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 307 updateExpressions(modelAnalyzer); 308 309 int counter = 0; 310 final Iterable<Expr> observables = filterObservables(modelAnalyzer); 311 List<String> flagMapping = new ArrayList<String>(); 312 mObservables = new ArrayList<Expr>(); 313 for (Expr expr : observables) { 314 // observables gets initial ids 315 flagMapping.add(expr.getUniqueKey()); 316 expr.setId(counter++); 317 mObservables.add(expr); 318 notifiableExpressions.add(expr); 319 L.d("observable %s", expr.getUniqueKey()); 320 } 321 322 // non-observable identifiers gets next ids 323 final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer); 324 for (Expr expr : nonObservableIds) { 325 flagMapping.add(expr.getUniqueKey()); 326 expr.setId(counter++); 327 notifiableExpressions.add(expr); 328 L.d("non-observable %s", expr.getUniqueKey()); 329 } 330 331 // descendants of observables gets following ids 332 for (Expr expr : observables) { 333 for (Expr parent : expr.getParents()) { 334 if (parent.hasId()) { 335 continue;// already has some id, means observable 336 } 337 // only fields earn an id 338 if (parent instanceof FieldAccessExpr) { 339 FieldAccessExpr fae = (FieldAccessExpr) parent; 340 L.d("checking field access expr %s. getter: %s", fae,fae.getGetter()); 341 if (fae.isDynamic() && fae.getGetter().canBeInvalidated()) { 342 flagMapping.add(parent.getUniqueKey()); 343 parent.setId(counter++); 344 notifiableExpressions.add(parent); 345 L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(), 346 Integer.toHexString(System.identityHashCode(parent)), 347 expr.getUniqueKey(), 348 Integer.toHexString(System.identityHashCode(expr))); 349 } 350 } 351 } 352 } 353 354 // now all 2-way bound view fields 355 for (Expr expr : mExprMap.values()) { 356 if (expr instanceof FieldAccessExpr) { 357 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) expr; 358 if (fieldAccessExpr.getChild() instanceof ViewFieldExpr) { 359 flagMapping.add(fieldAccessExpr.getUniqueKey()); 360 fieldAccessExpr.setId(counter++); 361 } 362 } 363 } 364 365 // non-dynamic binding expressions receive some ids so that they can be invalidated 366 L.d("list of binding expressions"); 367 for (int i = 0; i < mBindingExpressions.size(); i++) { 368 L.d("[%d] %s", i, mBindingExpressions.get(i)); 369 } 370 // we don't assign ids to constant binding expressions because now invalidateAll has its own 371 // flag. 372 373 for (Expr expr : notifiableExpressions) { 374 expr.enableDirectInvalidation(); 375 } 376 377 // make sure all dependencies are resolved to avoid future race conditions 378 for (Expr expr : mExprMap.values()) { 379 expr.getDependencies(); 380 } 381 mInvalidateAnyFlagIndex = counter ++; 382 flagMapping.add("INVALIDATE ANY"); 383 mInvalidateableFieldLimit = counter; 384 mInvalidateableFlags = new BitSet(); 385 for (int i = 0; i < mInvalidateableFieldLimit; i++) { 386 mInvalidateableFlags.set(i, true); 387 } 388 389 // make sure all dependencies are resolved to avoid future race conditions 390 for (Expr expr : mExprMap.values()) { 391 if (expr.isConditional()) { 392 L.d("requirement id for %s is %d", expr, counter); 393 expr.setRequirementId(counter); 394 flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX); 395 flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX); 396 counter += 2; 397 } 398 } 399 mConditionalFlags = new BitSet(); 400 for (int i = mInvalidateableFieldLimit; i < counter; i++) { 401 mConditionalFlags.set(i, true); 402 } 403 mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2; 404 405 // everybody gets an id 406 for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) { 407 final Expr value = entry.getValue(); 408 if (!value.hasId()) { 409 value.setId(counter++); 410 } 411 } 412 413 mFlagMapping = new String[flagMapping.size()]; 414 flagMapping.toArray(mFlagMapping); 415 416 mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize); 417 mInvalidateAnyFlags = new BitSet(); 418 mInvalidateAnyFlags.set(mInvalidateAnyFlagIndex, true); 419 420 for (Expr expr : mExprMap.values()) { 421 expr.getShouldReadFlagsWithConditionals(); 422 } 423 424 for (Expr expr : mExprMap.values()) { 425 // ensure all types are calculated 426 expr.getResolvedType(); 427 } 428 429 mSealed = true; 430 } 431 432 /** 433 * Run updateExpr on each binding expression until no new expressions are added. 434 * <p> 435 * Some expressions (e.g. field access) may replace themselves and add/remove new dependencies 436 * so we need to make sure each expression's update is called at least once. 437 */ 438 private void updateExpressions(ModelAnalyzer modelAnalyzer) { 439 int startSize = -1; 440 while (startSize != mExprMap.size()) { 441 startSize = mExprMap.size(); 442 ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions); 443 for (Expr expr : exprs) { 444 expr.updateExpr(modelAnalyzer); 445 } 446 } 447 } 448 449 public int getFlagBucketCount() { 450 return mFlagBucketCount; 451 } 452 453 public int getTotalFlagCount() { 454 return mRequirementIdCount * 2 + mInvalidateableFieldLimit; 455 } 456 457 public int getInvalidateableFieldLimit() { 458 return mInvalidateableFieldLimit; 459 } 460 461 public String[] getFlagMapping() { 462 return mFlagMapping; 463 } 464 465 public String getFlag(int id) { 466 return mFlagMapping[id]; 467 } 468 469 private List<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) { 470 List<Expr> result = new ArrayList<Expr>(); 471 for (Expr input : mExprMap.values()) { 472 if (input instanceof IdentifierExpr 473 && !input.hasId() 474 && !input.isObservable() 475 && input.isDynamic()) { 476 result.add(input); 477 } 478 } 479 return result; 480 } 481 482 private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) { 483 List<Expr> result = new ArrayList<Expr>(); 484 for (Expr input : mExprMap.values()) { 485 if (input.isObservable()) { 486 result.add(input); 487 } 488 } 489 return result; 490 } 491 492 public List<Expr> getPendingExpressions() { 493 if (mPendingExpressions == null) { 494 mPendingExpressions = new ArrayList<Expr>(); 495 for (Expr expr : mExprMap.values()) { 496 // if an expression is NOT dynanic but has conditional dependants, still return it 497 // so that conditional flags can be set 498 if (!expr.isRead() && (expr.isDynamic() || expr.hasConditionalDependant())) { 499 mPendingExpressions.add(expr); 500 } 501 } 502 } 503 return mPendingExpressions; 504 } 505 506 public boolean markBitsRead() { 507 // each has should read flags, we set them back on them 508 List<Expr> markedSomeFlagsRead = new ArrayList<Expr>(); 509 for (Expr expr : filterShouldRead(getPendingExpressions())) { 510 expr.markFlagsAsRead(expr.getShouldReadFlags()); 511 markedSomeFlagsRead.add(expr); 512 } 513 return pruneDone(markedSomeFlagsRead); 514 } 515 516 private boolean pruneDone(List<Expr> markedSomeFlagsAsRead) { 517 boolean marked = true; 518 List<Expr> markedAsReadList = new ArrayList<Expr>(); 519 while (marked) { 520 marked = false; 521 for (Expr expr : mExprMap.values()) { 522 if (expr.isRead()) { 523 continue; 524 } 525 if (expr.markAsReadIfDone()) { 526 L.d("marked %s as read ", expr.getUniqueKey()); 527 marked = true; 528 markedAsReadList.add(expr); 529 markedSomeFlagsAsRead.remove(expr); 530 } 531 } 532 } 533 boolean elevated = false; 534 for (Expr markedAsRead : markedAsReadList) { 535 for (Dependency dependency : markedAsRead.getDependants()) { 536 if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) { 537 elevated = true; 538 } 539 } 540 } 541 for (Expr partialRead : markedSomeFlagsAsRead) { 542 // even if all paths are not satisfied, we can elevate certain conditional dependencies 543 // if all of their paths are satisfied. 544 for (Dependency dependency : partialRead.getDependants()) { 545 Expr dependant = dependency.getDependant(); 546 if (dependant.isConditional() && dependant.getAllCalculationPaths() 547 .areAllPathsSatisfied(partialRead.mReadSoFar)) { 548 if (dependant.considerElevatingConditionals(partialRead)) { 549 elevated = true; 550 } 551 } 552 } 553 } 554 if (elevated) { 555 // some conditionals are elevated. We should re-calculate flags 556 for (Expr expr : getPendingExpressions()) { 557 if (!expr.isRead()) { 558 expr.invalidateReadFlags(); 559 } 560 } 561 mPendingExpressions = null; 562 } 563 return elevated; 564 } 565 566 private static boolean hasConditionalOrNestedCannotReadDependency(Expr expr) { 567 for (Dependency dependency : expr.getDependencies()) { 568 if (dependency.isConditional() || dependency.getOther().hasNestedCannotRead()) { 569 return true; 570 } 571 } 572 return false; 573 } 574 575 public static List<Expr> filterShouldRead(Iterable<Expr> exprs) { 576 List<Expr> result = new ArrayList<Expr>(); 577 for (Expr expr : exprs) { 578 if (!expr.getShouldReadFlags().isEmpty() && 579 !hasConditionalOrNestedCannotReadDependency(expr)) { 580 result.add(expr); 581 } 582 } 583 return result; 584 } 585 586 /** 587 * May return null if flag is equal to invalidate any flag. 588 */ 589 public Expr findFlagExpression(int flag) { 590 if (mInvalidateAnyFlags.get(flag)) { 591 return null; 592 } 593 final String key = mFlagMapping[flag]; 594 if (mExprMap.containsKey(key)) { 595 return mExprMap.get(key); 596 } 597 int falseIndex = key.indexOf(FALSE_KEY_SUFFIX); 598 if (falseIndex > -1) { 599 final String trimmed = key.substring(0, falseIndex); 600 return mExprMap.get(trimmed); 601 } 602 int trueIndex = key.indexOf(TRUE_KEY_SUFFIX); 603 if (trueIndex > -1) { 604 final String trimmed = key.substring(0, trueIndex); 605 return mExprMap.get(trimmed); 606 } 607 // log everything we call 608 StringBuilder error = new StringBuilder(); 609 error.append("cannot find flag:").append(flag).append("\n"); 610 error.append("invalidate any flag:").append(mInvalidateAnyFlags).append("\n"); 611 error.append("key:").append(key).append("\n"); 612 error.append("flag mapping:").append(Arrays.toString(mFlagMapping)); 613 L.e(error.toString()); 614 return null; 615 } 616 617 public BitSet getInvalidateAnyBitSet() { 618 return mInvalidateAnyFlags; 619 } 620 621 public int getInvalidateAnyFlagIndex() { 622 return mInvalidateAnyFlagIndex; 623 } 624 625 public Expr argListExpr(Iterable<Expr> expressions) { 626 return register(new ArgListExpr(mArgListIdCounter ++, expressions)); 627 } 628 629 public void setCurrentLocationInFile(Location location) { 630 mCurrentLocationInFile = location; 631 } 632 633 public Expr listenerExpr(Expr expression, String name, ModelClass listenerType, 634 ModelMethod listenerMethod) { 635 return register(new ListenerExpr(expression, name, listenerType, listenerMethod)); 636 } 637 } 638