1 /* 2 * Copyright (C) 2016 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 android.databinding.tool.reflection.Callable 20 import android.databinding.tool.solver.ExecutionPath 21 import android.databinding.tool.writer.KCode 22 import android.databinding.tool.writer.fieldName 23 import android.databinding.tool.writer.isForcedToLocalize 24 import android.databinding.tool.writer.isVariable 25 import android.databinding.tool.writer.kcode 26 import android.databinding.tool.writer.scopedName 27 28 fun Expr.shouldLocalizeInCallbacks() = canBeEvaluatedToAVariable() && !resolvedType.isVoid && (isDynamic || isForcedToLocalize()) 29 30 fun CallbackExprModel.localizeGlobalVariables(vararg ignore: Expr): KCode = kcode("// localize variables for thread safety") { 31 // puts all variables in this model to local values. 32 mExprMap.values.filter { it.shouldLocalizeInCallbacks() && !ignore.contains(it) }.forEach { 33 nl("// ${it.toString()}") 34 nl("${it.resolvedType.toJavaCode()} ${it.scopedName()} = ${if (it.isVariable()) it.fieldName else it.defaultValue};") 35 } 36 } 37 38 fun ExecutionPath.toCode(): KCode = kcode("") { 39 val myExpr = expr 40 if (myExpr != null && !isAlreadyEvaluated) { 41 // variables are read up top 42 val localize = myExpr.shouldLocalizeInCallbacks() && !myExpr.isVariable() 43 // if this is not a method call (or method call via field access, don't do anything 44 val eligible = localize || (myExpr is MethodCallExpr || (myExpr is FieldAccessExpr && myExpr.getter.type == Callable.Type.METHOD)) 45 if (eligible) { 46 val assign = if (localize) { 47 "${myExpr.scopedName()} = " 48 } else { 49 "" 50 } 51 if (myExpr is TernaryExpr) { 52 // if i know the value, short circuit it 53 if (knownValues.containsKey(myExpr.pred)) { 54 val chosen = if (knownValues[myExpr.pred]!!) myExpr.ifTrue else myExpr.ifFalse 55 // fast read me 56 nl("$assign${chosen.toCode().generate()};") 57 } else { 58 // read me 59 nl("$assign${myExpr.toFullCode().generate()};") 60 } 61 } else { 62 // read me 63 nl("$assign${myExpr.toFullCode().generate()};") 64 } 65 } 66 } 67 children.forEach { 68 nl(it.toCode()) 69 } 70 // if i have branches, execute them 71 val myTrue = trueBranch 72 val myFalse = falseBranch 73 if (myTrue != null) { 74 val condition = with(myTrue.conditional) { 75 if (shouldLocalizeInCallbacks()) { 76 scopedName() 77 } else { 78 toFullCode().generate() 79 } 80 } 81 block("if ($condition)") { 82 nl(myTrue.path.toCode()) 83 } 84 if (myFalse != null) { 85 block("else") { 86 nl(myFalse.path.toCode()) 87 } 88 } 89 } 90 }