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 17package android.databinding.tool.expr 18 19import android.databinding.tool.reflection.Callable 20import android.databinding.tool.solver.ExecutionPath 21import android.databinding.tool.writer.KCode 22import android.databinding.tool.writer.fieldName 23import android.databinding.tool.writer.isForcedToLocalize 24import android.databinding.tool.writer.isVariable 25import android.databinding.tool.writer.kcode 26import android.databinding.tool.writer.scopedName 27 28fun Expr.shouldLocalizeInCallbacks() = canBeEvaluatedToAVariable() && !resolvedType.isVoid && (isDynamic || isForcedToLocalize()) 29 30fun 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 38fun 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}