1d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar/* 2d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 3d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * 4d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * you may not use this file except in compliance with the License. 6d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * You may obtain a copy of the License at 7d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * 8d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * 10d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * See the License for the specific language governing permissions and 14d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * limitations under the License. 15d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar */ 16d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.expr; 18d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 19731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.processing.Scope; 20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable; 21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable.Type; 22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer; 23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass; 24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelMethod; 256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.solver.ExecutionPath; 26fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L; 27e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mountimport android.databinding.tool.writer.KCode; 28d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 29d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.ArrayList; 306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.Arrays; 31d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.List; 32e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount 336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport static android.databinding.tool.reflection.Callable.DYNAMIC; 346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport static android.databinding.tool.reflection.Callable.STATIC; 356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar 36d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 37d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarpublic class MethodCallExpr extends Expr { 38d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar final String mName; 39e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount Callable mGetter; 40bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount // Allow protected calls -- only used for ViewDataBinding methods. 41bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount private boolean mAllowProtected; 42d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 432611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar static List<Expr> concat(Expr e, List<Expr> list) { 449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar List<Expr> merged = new ArrayList<Expr>(); 452611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar merged.add(e); 462611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar merged.addAll(list); 472611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar return merged; 482611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar } 492611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar 50d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar MethodCallExpr(Expr target, String name, List<Expr> args) { 512611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar super(concat(target, args)); 52d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar mName = name; 53d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 54d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 556047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar @SuppressWarnings("Duplicates") 56d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar @Override 5718243f6f1b7527272ef4feccdf4327d80d9f2241George Mount public void updateExpr(ModelAnalyzer modelAnalyzer) { 58731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar try { 59731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar Scope.enter(this); 60731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar resolveType(modelAnalyzer); 61731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar super.updateExpr(modelAnalyzer); 62731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar } finally { 63731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar Scope.exit(); 64731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar } 6518243f6f1b7527272ef4feccdf4327d80d9f2241George Mount } 6618243f6f1b7527272ef4feccdf4327d80d9f2241George Mount 6718243f6f1b7527272ef4feccdf4327d80d9f2241George Mount @Override 68bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount protected KCode generateCode() { 69e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount KCode code = new KCode() 70bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount .app("", getTarget().toCode()) 716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar .app(".") 726047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar .app(getGetter().name) 736047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar .app("("); 74bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount appendArgs(code); 756047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar code.app(")"); 766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar return code; 776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar 79bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount @Override 80bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount public Expr cloneToModel(ExprModel model) { 81bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount return model.methodCall(getTarget().cloneToModel(model), mName, 82bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount cloneToModel(model, getArgs())); 83bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 84bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount 85bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount private void appendArgs(KCode code) { 86e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount boolean first = true; 87e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount for (Expr arg : getArgs()) { 88e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount if (first) { 89e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount first = false; 90e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount } else { 91e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount code.app(", "); 92e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount } 93bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount code.app("", arg.toCode()); 94e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount } 956047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 966047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar 976047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar @Override 986047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) { 996047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths); 1006047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar // after this, we need a null check. 1016047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar List<ExecutionPath> result = new ArrayList<ExecutionPath>(); 1026047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar if (getTarget() instanceof StaticIdentifierExpr) { 1036047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar result.addAll(toExecutionPathInOrder(paths, getArgs())); 1046047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } else { 1056047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar for (ExecutionPath path : targetPaths) { 1066047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar Expr cmp = getModel() 1076047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar .comparison("!=", getTarget(), getModel().symbol("null", Object.class)); 1086047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar path.addPath(cmp); 1096047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar final ExecutionPath subPath = path.addBranch(cmp, true); 1106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar if (subPath != null) { 1116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar result.addAll(toExecutionPathInOrder(subPath, getArgs())); 1126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 1136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 1146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 1156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar return result; 1166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar } 1176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar 1186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar private List<ExecutionPath> toExecutionPathInOrder(ExecutionPath path, List<Expr> args) { 1196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar return toExecutionPathInOrder(Arrays.asList(path), args); 120e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount } 121e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount 122e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount @Override 12379fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 124d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar if (mGetter == null) { 125895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar List<ModelClass> args = new ArrayList<ModelClass>(); 1267920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount for (Expr expr : getArgs()) { 127d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar args.add(expr.getResolvedType()); 128d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 12918243f6f1b7527272ef4feccdf4327d80d9f2241George Mount 13018243f6f1b7527272ef4feccdf4327d80d9f2241George Mount Expr target = getTarget(); 131a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount boolean isStatic = target instanceof StaticIdentifierExpr; 132bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic, 133bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount mAllowProtected); 134fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount if (method == null) { 135bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount StringBuilder argTypes = new StringBuilder(); 136bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount for (ModelClass arg : args) { 137bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount if (argTypes.length() != 0) { 138bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount argTypes.append(", "); 139bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 140bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount argTypes.append(arg.toJavaCode()); 141bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 142bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount String message = "cannot find method '" + mName + "(" + argTypes + ")' in class " + 143fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount target.getResolvedType().toJavaCode(); 144fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount IllegalArgumentException e = new IllegalArgumentException(message); 145bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount L.e(e, "cannot find method %s(%s) in class %s", mName, argTypes, 146fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount target.getResolvedType().toJavaCode()); 147fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount throw e; 148fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount } 149ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar if (!isStatic && method.isStatic()) { 150ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar // found a static method on an instance. Use class instead 151ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar target.getParents().remove(this); 152ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar getChildren().remove(target); 153ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar StaticIdentifierExpr staticId = getModel() 154ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar .staticIdentifierFor(target.getResolvedType()); 155ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar getChildren().add(staticId); 156ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar staticId.getParents().add(this); 157ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar // make sure we update this in case we access it below 158ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar target = getTarget(); 159ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar } 160019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar int flags = DYNAMIC; 161019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar if (method.isStatic()) { 162019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar flags |= STATIC; 163019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar } 164d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount mGetter = new Callable(Type.METHOD, method.getName(), null, method.getReturnType(args), 1656047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar method.getParameterTypes().length, flags, method); 166d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 167d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar return mGetter.resolvedType; 168d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 169d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 170d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar @Override 171d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar protected List<Dependency> constructDependencies() { 172dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar final List<Dependency> dependencies = constructDynamicChildrenDependencies(); 173dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar for (Dependency dependency : dependencies) { 1747920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount if (dependency.getOther() == getTarget()) { 175dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar dependency.setMandatory(true); 176dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar } 177dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar } 178dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar return dependencies; 179d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 180d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 181d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar @Override 182d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar protected String computeUniqueKey() { 1832611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar return join(getTarget().computeUniqueKey(), mName, 1847920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount super.computeUniqueKey()); 185d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 186d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 187d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar public Expr getTarget() { 1887920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount return getChildren().get(0); 189d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 190d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 191d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar public String getName() { 192d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar return mName; 193d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 194d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 195d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar public List<Expr> getArgs() { 1967920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount return getChildren().subList(1, getChildren().size()); 197d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 198d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar 199e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount public Callable getGetter() { 200d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar return mGetter; 201d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar } 202d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount 203bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount public void setAllowProtected() { 204bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount mAllowProtected = true; 205bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 206bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount 207d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount @Override 208d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount public String getInvertibleError() { 209d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount return "Method calls may not be used in two-way expressions"; 210d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount } 211bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount 212bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount @Override 213bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount public String toString() { 214bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount StringBuilder buf = new StringBuilder(); 215bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount buf.append(getTarget()) 216bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount .append('.') 217bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount .append(mName) 218bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount .append('('); 219bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount final List<Expr> args = getArgs(); 220bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount for (int i = 0; i < args.size(); i++) { 221bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount Expr arg = args.get(i); 222bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount if (i != 0) { 223bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount buf.append(", "); 224bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 225bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount buf.append(arg); 226bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 227bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount buf.append(')'); 228bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount return buf.toString(); 229bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount } 230d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar} 231