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