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