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 org.eclipse.golo.compiler.PackageAndClass;
014
015import java.util.*;
016
017import static java.util.Collections.unmodifiableCollection;
018
019public final class GoloModule extends GoloElement<GoloModule> implements FunctionContainer {
020
021  private String sourceFile;
022  private final PackageAndClass packageAndClass;
023  private final ReferenceTable globalReferences;
024  private final List<ModuleImport> imports = new LinkedList<>();
025  private final Set<GoloFunction> functions = new LinkedHashSet<>();
026  private final Map<PackageAndClass, Augmentation> augmentations = new LinkedHashMap<>();
027  private final Set<NamedAugmentation> namedAugmentations = new LinkedHashSet<>();
028  private final Set<GoloType<?>> types = new LinkedHashSet<>();
029  private final Set<LocalReference> moduleState = new LinkedHashSet<>();
030  private GoloFunction moduleStateInitializer = null;
031  private boolean hasMain = false;
032
033  private static final ModuleImport[] DEFAULT_IMPORTS = {
034    ModuleImport.implicit("gololang.Predefined"),
035    ModuleImport.implicit("gololang.StandardAugmentations"),
036    ModuleImport.implicit("gololang"),
037    ModuleImport.implicit("java.lang")
038  };
039
040  public static final String MODULE_INITIALIZER_FUNCTION = "<clinit>";
041  public static final String TYPE_SUBPACKAGE = "types";
042
043  private GoloModule(PackageAndClass name, ReferenceTable references) {
044    super();
045    this.packageAndClass = name;
046    this.globalReferences = references;
047  }
048
049  public static GoloModule create(PackageAndClass name, ReferenceTable references) {
050    return new GoloModule(name,
051        references == null ?  new ReferenceTable() : references);
052  }
053
054  protected GoloModule self() { return this; }
055
056  public PackageAndClass getPackageAndClass() {
057    return packageAndClass;
058  }
059
060  public PackageAndClass getTypesPackage() {
061    return packageAndClass.createSubPackage(TYPE_SUBPACKAGE);
062  }
063
064  public String sourceFile() {
065    return this.sourceFile == null ? "unknown" : this.sourceFile;
066  }
067
068  public GoloModule sourceFile(String file) {
069    this.sourceFile = file;
070    return this;
071  }
072
073  public ReferenceTable getReferenceTable() {
074    return this.globalReferences;
075  }
076
077  /**
078   * {@inheritDoc}
079   *
080   * @return the module itself.
081   */
082  @Override
083  public GoloModule enclosingModule() {
084    return this;
085  }
086
087  public Set<ModuleImport> getImports() {
088    Set<ModuleImport> imp = new LinkedHashSet<>();
089    if (!types.isEmpty()) {
090      imp.add(ModuleImport.implicit(this.getTypesPackage()));
091      for (GoloType<?> t : types) {
092        if (t instanceof Union) {
093          imp.add(ModuleImport.implicit(t.getPackageAndClass()));
094        }
095      }
096    }
097    imp.addAll(imports);
098    if (this.packageAndClass.hasPackage()) {
099      imp.add(ModuleImport.implicit(this.packageAndClass.parentPackage()));
100    }
101    Collections.addAll(imp, DEFAULT_IMPORTS);
102    return imp;
103  }
104
105  public Collection<Augmentation> getAugmentations() {
106    return unmodifiableCollection(augmentations.values());
107  }
108
109  /**
110   * {@inheritDoc}
111   */
112  @Override
113  public List<GoloFunction> getFunctions() {
114    return new ArrayList<>(functions);
115  }
116
117  /**
118   * {@inheritDoc}
119   */
120  @Override
121  public boolean hasFunctions() {
122    return !functions.isEmpty();
123  }
124
125  public boolean hasMain() {
126    return hasMain;
127  }
128
129  /**
130   * {@inheritDoc}
131   */
132  @Override
133  public void addFunction(GoloFunction function) {
134    function.getBlock().getReferenceTable().relinkTopLevel(globalReferences);
135    functions.add(makeParentOf(function));
136    if (function.isMain()) {
137      hasMain = true;
138    }
139  }
140
141  private void addAugmentation(Augmentation augment) {
142    PackageAndClass target = augment.getTarget();
143    if (augmentations.containsKey(target)) {
144      augmentations.get(target).merge(augment);
145    } else {
146      augmentations.put(target, augment);
147    }
148  }
149
150  public GoloElement<?> getSubtypeByName(String name) {
151    if (name == null) { return null; }
152    for (GoloType<?> t : types) {
153      if (name.equals(t.getName())) {
154        return t;
155      }
156    }
157    return null;
158  }
159
160  private void addModuleStateInitializer(AssignmentStatement assignment) {
161    if (!assignment.isDeclaring()) {
162      throw new IllegalArgumentException("Module state must be a declaring assignment");
163    }
164    assignment.getLocalReference().moduleLevel();
165    this.moduleState.add(assignment.getLocalReference());
166    if (moduleStateInitializer == null) {
167      moduleStateInitializer = GoloFunction.function(MODULE_INITIALIZER_FUNCTION)
168        .block(Block.empty().ref(globalReferences));
169      functions.add(moduleStateInitializer);
170    }
171    moduleStateInitializer.getBlock().add(assignment);
172  }
173
174  public GoloModule add(GoloElement<?> element) {
175    if (element == null || element instanceof Noop) {
176      return this;
177    }
178    if (element instanceof ToplevelElements) {
179      for (GoloElement<?> e : (ToplevelElements) element) {
180        this.add(e);
181      }
182      return this;
183    }
184    makeParentOf(element);
185    if (element instanceof ModuleImport) {
186      imports.add((ModuleImport) element);
187    } else if (element instanceof GoloFunction) {
188      addFunction((GoloFunction) element);
189    } else if (element instanceof GoloType<?>) {
190      GoloType<?> t = (GoloType<?>) element;
191      types.add(t);
192    } else if (element instanceof Augmentation) {
193      addAugmentation((Augmentation) element);
194    } else if (element instanceof NamedAugmentation) {
195      namedAugmentations.add((NamedAugmentation) element);
196    } else if (element instanceof LocalReference) {
197      this.moduleState.add((LocalReference) element);
198    } else if (element instanceof AssignmentStatement) {
199      addModuleStateInitializer((AssignmentStatement) element);
200    } else {
201      throw new IllegalArgumentException("Can't add a " + element.getClass() + " to a module");
202    }
203    return this;
204  }
205
206  /**
207   * {@inheritDoc}
208   */
209  @Override
210  public void accept(GoloIrVisitor visitor) {
211    visitor.visitModule(this);
212  }
213
214  /**
215   * {@inheritDoc}
216   */
217  @Override
218  public List<GoloElement<?>> children() {
219    LinkedList<GoloElement<?>> children = new LinkedList<>();
220    children.addAll(getImports());
221    children.addAll(types);
222    children.addAll(augmentations.values());
223    children.addAll(namedAugmentations);
224    children.addAll(moduleState);
225    children.addAll(functions);
226    return children;
227  }
228
229  public boolean isEmpty() {
230    return imports.isEmpty()
231        && types.isEmpty()
232        && augmentations.isEmpty()
233        && namedAugmentations.isEmpty()
234        && moduleState.isEmpty()
235        && functions.isEmpty();
236  }
237
238  /**
239   * {@inheritDoc}
240   */
241  @Override
242  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
243    if (original instanceof GoloFunction) {
244      this.functions.remove(original);
245    } else {
246      throw cantReplace(original, newElement);
247    }
248    this.add(newElement);
249  }
250
251  @Override
252  public String toString() {
253    return String.format("GoloModule{name=%s, src=%s}", this.packageAndClass, this.sourceFile);
254  }
255}