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}