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