001/* 002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon) 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.golo.compiler.ir; 011 012import java.util.LinkedList; 013import java.util.List; 014import java.util.HashSet; 015import java.util.Set; 016import java.util.Collection; 017 018import org.eclipse.golo.compiler.parser.GoloASTNode; 019 020import static java.util.Collections.unmodifiableList; 021import static java.util.Arrays.asList; 022import java.util.Objects; 023import static org.eclipse.golo.compiler.ir.Builders.*; 024import static java.util.Objects.requireNonNull; 025 026public final class GoloFunction extends ExpressionStatement implements Scope { 027 028 private static final SymbolGenerator SYMBOLS = new SymbolGenerator("function"); 029 030 private String name; 031 private boolean isLocal = false; 032 private Scope scope = Scope.MODULE; 033 034 private final List<String> parameterNames = new LinkedList<>(); 035 private final List<String> syntheticParameterNames = new LinkedList<>(); 036 private boolean varargs = false; 037 private Block block; 038 private boolean synthetic = false; 039 private boolean decorator = false; 040 private String syntheticSelfName = null; 041 private String decoratorRef = null; 042 private final LinkedList<Decorator> decorators = new LinkedList<>(); 043 044 public static enum Scope { 045 MODULE, AUGMENT, CLOSURE 046 } 047 048 GoloFunction() { 049 super(); 050 block = Builders.block(); 051 makeParentOf(block); 052 } 053 054 @Override 055 public GoloFunction ofAST(GoloASTNode n) { 056 super.ofAST(n); 057 return this; 058 } 059 060 // name ----------------------------------------------------------------------------------------- 061 public GoloFunction name(String n) { 062 this.name = n; 063 return this; 064 } 065 066 public String getName() { 067 return name; 068 } 069 070 public boolean isMain() { 071 return "main".equals(name) && getArity() == 1; 072 } 073 074 public boolean isModuleInit() { 075 return GoloModule.MODULE_INITIALIZER_FUNCTION.equals(this.name); 076 } 077 078 public boolean isAnonymous() { 079 return this.name == null; 080 } 081 082 // synthetic ------------------------------------------------------------------------------------ 083 public GoloFunction synthetic() { 084 this.synthetic = true; 085 return this; 086 } 087 088 public boolean isSynthetic() { 089 return synthetic; 090 } 091 092 // decorator ------------------------------------------------------------------------------------ 093 public GoloFunction decorator() { 094 return decorator(true); 095 } 096 097 public GoloFunction decorator(boolean d) { 098 this.decorator = d; 099 return this; 100 } 101 102 public boolean isDecorator() { 103 return decorator; 104 } 105 106 // visibility ----------------------------------------------------------------------------------- 107 public GoloFunction local() { 108 return local(true); 109 } 110 111 public GoloFunction local(boolean isLocal) { 112 this.isLocal = isLocal; 113 return this; 114 } 115 116 public boolean isLocal() { 117 return this.isLocal; 118 } 119 120 // scope ---------------------------------------------------------------------------------------- 121 public GoloFunction inScope(GoloFunction.Scope s) { 122 this.scope = s; 123 return this; 124 } 125 126 public GoloFunction inAugment() { 127 return inAugment(true); 128 } 129 130 public GoloFunction inAugment(boolean isInAugment) { 131 if (isInAugment) { 132 this.scope = Scope.AUGMENT; 133 } 134 return this; 135 } 136 137 public boolean isInAugment() { 138 return Scope.AUGMENT.equals(scope); 139 } 140 141 public GoloFunction asClosure() { 142 this.scope = Scope.CLOSURE; 143 return this; 144 } 145 146 // block ---------------------------------------------------------------------------------------- 147 public GoloFunction block(Object... statements) { 148 return this.block(Builders.block(statements)); 149 } 150 151 public GoloFunction block(Block block) { 152 this.block = requireNonNull(block); 153 for (String param : parameterNames) { 154 addParameterToBlockReferences(param); 155 } 156 makeParentOf(this.block); 157 return this; 158 } 159 160 public Block getBlock() { 161 return block; 162 } 163 164 public GoloFunction returns(Object expression) { 165 this.block.add(Builders.returns(expression)); 166 return this; 167 } 168 169 public void insertMissingReturnStatement() { 170 if (!this.block.hasReturn() && !this.isModuleInit()) { 171 ReturnStatement missingReturnStatement = Builders.returns(constant(null)).synthetic(); 172 if (this.isMain()) { 173 missingReturnStatement.returningVoid(); 174 } 175 this.block.addStatement(missingReturnStatement); 176 } 177 } 178 179 // parameters and varargs ----------------------------------------------------------------------- 180 public GoloFunction varargs(boolean isVarargs) { 181 this.varargs = isVarargs; 182 return this; 183 } 184 185 public GoloFunction varargs() { 186 return this.varargs(true); 187 } 188 189 public boolean isVarargs() { 190 return varargs; 191 } 192 193 public int getArity() { 194 return parameterNames.size() + syntheticParameterNames.size(); 195 } 196 197 public GoloFunction withParameters(String... names) { 198 return withParameters(asList(names)); 199 } 200 201 public GoloFunction withParameters(Collection<String> names) { 202 for (String name : names) { 203 addParameterToBlockReferences(name); 204 this.parameterNames.add(name); 205 } 206 return this; 207 } 208 209 private void addParameterToBlockReferences(String name) { 210 this.block.getReferenceTable().add(localRef(name)); 211 } 212 213 public int getSyntheticParameterCount() { 214 return syntheticParameterNames.size(); 215 } 216 217 public List<String> getParameterNames() { 218 LinkedList<String> list = new LinkedList<>(syntheticParameterNames); 219 list.addAll(parameterNames); 220 return unmodifiableList(list); 221 } 222 223 public List<String> getSyntheticParameterNames() { 224 return unmodifiableList(syntheticParameterNames); 225 } 226 227 public void addSyntheticParameters(Set<String> names) { 228 Set<String> existing = new HashSet<>(getParameterNames()); 229 for (String name : names) { 230 if (!existing.contains(name) && !name.equals(syntheticSelfName)) { 231 LocalReference ref = block.getReferenceTable().get(name); 232 if (ref == null || !ref.isModuleState()) { 233 this.syntheticParameterNames.add(name); 234 } 235 } 236 } 237 } 238 239 public String getSyntheticSelfName() { 240 return syntheticSelfName; 241 } 242 243 public void setSyntheticSelfName(String name) { 244 if (syntheticParameterNames.contains(name)) { 245 this.syntheticParameterNames.remove(name); 246 this.syntheticSelfName = name; 247 } 248 } 249 250 public void captureClosedReference() { 251 if (synthetic && syntheticSelfName != null) { 252 LocalReference self = block.getReferenceTable().get(syntheticSelfName); 253 ClosureReference closureReference = asClosureReference(); 254 closureReference.updateCapturedReferenceNames(); 255 block.prependStatement(Builders.define(self).as(closureReference)); 256 } 257 } 258 259 // decorators ----------------------------------------------------------------------------------- 260 public GoloFunction decoratedWith(Object... decorators) { 261 for (Object deco : decorators) { 262 if (deco instanceof Decorator) { 263 this.addDecorator((Decorator) deco); 264 } else { 265 throw cantConvert("Decorator", deco); 266 } 267 } 268 return this; 269 } 270 271 public boolean isDecorated() { 272 return !decorators.isEmpty(); 273 } 274 275 public String getDecoratorRef() { 276 return decoratorRef; 277 } 278 279 public void addDecorator(Decorator decorator) { 280 this.decorators.add(decorator); 281 makeParentOf(decorator); 282 } 283 284 public List<Decorator> getDecorators() { 285 return unmodifiableList(decorators); 286 } 287 288 public boolean hasDecorators() { 289 return !decorators.isEmpty(); 290 } 291 292 public GoloFunction createDecorator() { 293 ExpressionStatement expr = refLookup("__$$_original"); 294 for (Decorator decorator : this.getDecorators()) { 295 expr = decorator.wrapExpression(expr); 296 } 297 this.decoratorRef = SYMBOLS.next(name + "_decorator"); 298 return functionDeclaration(this.decoratorRef) 299 .decorator() 300 .inScope(this.scope) 301 .withParameters("__$$_original") 302 .returns(expr); 303 } 304 305 //----------------------------------------------------------------------------------------------- 306 public ClosureReference asClosureReference() { 307 if (scope != Scope.CLOSURE) { 308 throw new IllegalStateException("Can't get a closure reference of a non-closure function"); 309 } 310 return new ClosureReference(this); 311 } 312 313 @Override 314 public void relink(ReferenceTable table) { 315 block.relink(table); 316 } 317 318 @Override 319 public void relinkTopLevel(ReferenceTable table) { 320 block.relinkTopLevel(table); 321 } 322 323 @Override 324 public String toString() { 325 return String.format("Function<name=%s, arity=%d, vararg=%s>", 326 getName(), 327 getArity(), 328 isVarargs()); 329 } 330 331 @Override 332 public void accept(GoloIrVisitor visitor) { 333 visitor.visitFunction(this); 334 } 335 336 @Override 337 public void walk(GoloIrVisitor visitor) { 338 for (Decorator deco : decorators) { 339 deco.accept(visitor); 340 } 341 block.accept(visitor); 342 } 343 344 @Override 345 protected void replaceElement(GoloElement original, GoloElement newElement) { 346 throw cantReplace(); 347 } 348 349 @Override 350 public int hashCode() { 351 return Objects.hash(this.name, this.getArity(), this.varargs); 352 } 353 354 @Override 355 public boolean equals(Object obj) { 356 if (obj == this) { 357 return true; 358 } 359 if (obj == null) { 360 return false; 361 } 362 if (getClass() != obj.getClass()) { 363 return false; 364 } 365 final GoloFunction other = (GoloFunction) obj; 366 if (!Objects.equals(this.name, other.name)) { 367 return false; 368 } 369 if (this.varargs != other.varargs) { 370 return false; 371 } 372 return this.getArity() == other.getArity(); 373 } 374}