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