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