001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang.ir; 012 013import java.util.Collections; 014import java.util.LinkedList; 015import java.util.List; 016 017import org.eclipse.golo.compiler.PackageAndClass; 018 019/** 020 * Abstract representation of a Golo invocation in the IR tree. 021 * <p> 022 * Used to represent regular function call and method invocation. 023 * <p> 024 * Note that the called object is only refereed by its name and not an IR node, since the linkage is done at runtime. 025 * There is no guaranty that such an object exists. 026 */ 027public abstract class AbstractInvocation<T extends AbstractInvocation<T>> extends ExpressionStatement<T> { 028 029 private PackageAndClass packageAndClass; 030 private final List<GoloElement<?>> arguments = new LinkedList<>(); 031 protected boolean usesNamedArguments = false; 032 033 AbstractInvocation(String name) { 034 super(); 035 this.packageAndClass = PackageAndClass.of(name); 036 } 037 038 /** 039 * Returns the fully qualified name of the called object. 040 */ 041 public String getName() { 042 return packageAndClass.toString(); 043 } 044 045 /** 046 * Returns the module part of the called object name. 047 */ 048 public String getModuleName() { 049 if (packageAndClass == null) { return ""; } 050 return packageAndClass.packageName(); 051 } 052 053 /** 054 * Returns the object part of the called object name. 055 */ 056 public String getFunctionName() { 057 if (packageAndClass == null) { return ""; } 058 return packageAndClass.className(); 059 } 060 061 public void setPackageAndClass(PackageAndClass name) { 062 this.packageAndClass = name; 063 } 064 065 public PackageAndClass getPackageAndClass() { 066 return this.packageAndClass; 067 } 068 069 private void addArgument(GoloElement<?> argument) { 070 arguments.add(makeParentOf(argument)); 071 if (argument instanceof NamedArgument) { 072 withNamedArguments(); 073 } 074 } 075 076 /** 077 * Defines the values of the arguments for this invocation. 078 * 079 * <p>This is a builder method. 080 * <p>This methods <em>appends</em> the arguments to the existing ones, so that it can be called multiple times to 081 * create the invocation incrementally. On the other hand, it is <em>not</em> idempotent. 082 * <p>Calls {@link #withNamedArguments()} if needed. 083 * 084 * @param arguments the arguments of the invocation. An argument can be any {@link GoloElement}, or are converted with 085 * {@link ExpressionStatement#of(Object)} otherwise, so that strings and primitives are automatically wrapped in a 086 * {@link ConstantStatement}. 087 * @return this invocation 088 * @see #withNamedArguments() 089 * @see ExpressionStatement#of(Object) 090 */ 091 public T withArgs(Object... arguments) { 092 for (Object argument : arguments) { 093 if (argument instanceof GoloElement) { 094 addArgument((GoloElement) argument); 095 } else { 096 addArgument(ExpressionStatement.of(argument)); 097 } 098 } 099 return self(); 100 } 101 102 public List<GoloElement<?>> getArguments() { 103 return Collections.unmodifiableList(arguments); 104 } 105 106 /** 107 * Returns the number or arguments of this invocation. 108 * 109 * <p>It can be different of called object number of parameters if it is a vararg one. 110 * <p>Since the call is resolved at runtime, there is no guaranty that these two numbers match. 111 */ 112 public int getArity() { 113 return arguments.size(); 114 } 115 116 /** 117 * Checks if this call uses the named arguments syntax. 118 * <p>See the 119 * <a href="http://golo-lang.org/documentation/next/index.html#_named_parameters">Golo Guide on Named Parameters</a> 120 */ 121 public boolean usesNamedArguments() { 122 return usesNamedArguments; 123 } 124 125 public boolean namedArgumentsComplete() { 126 return this.arguments.isEmpty() || this.usesNamedArguments; 127 } 128 129 /** 130 * Mark the invocation as using names arguments syntax. 131 * 132 * <p>This is a builder method. 133 * 134 * <p>This method should not need to be called directly since the {@link #withArgs(Object...)} one checks its argument 135 * and call this one if needed. 136 * 137 * @see #withArgs(Object...) 138 */ 139 public T withNamedArguments() { 140 this.usesNamedArguments = true; 141 return self(); 142 } 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override 148 public List<GoloElement<?>> children() { 149 return Collections.unmodifiableList(arguments); 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 157 if (arguments.contains(original)) { 158 this.arguments.set(arguments.indexOf(original), newElement); 159 makeParentOf(newElement); 160 if (newElement instanceof NamedArgument) { 161 withNamedArguments(); 162 } 163 } else { 164 throw cantReplace(original, newElement); 165 } 166 } 167}