001/* 002 * Copyright (c) 2012-2021 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 final Set<MacroInvocation> topLevelMacroInvocations = new LinkedHashSet<>(); 031 private MacroInvocation decoratorMacro = null; 032 private GoloFunction moduleStateInitializer = null; 033 private boolean hasMain = false; 034 private boolean hasMacros = false; 035 036 private static final ModuleImport[] DEFAULT_IMPORTS = { 037 ModuleImport.implicit("gololang.Predefined"), 038 ModuleImport.implicit("gololang.StandardAugmentations"), 039 ModuleImport.implicit("gololang"), 040 ModuleImport.implicit("java.lang") 041 }; 042 043 public static final String MODULE_INITIALIZER_FUNCTION = "<clinit>"; 044 public static final String TYPE_SUBPACKAGE = "types"; 045 046 private GoloModule(PackageAndClass name, ReferenceTable references) { 047 super(); 048 this.packageAndClass = name; 049 this.globalReferences = references; 050 } 051 052 public static GoloModule create(PackageAndClass name, ReferenceTable references) { 053 return new GoloModule(name, 054 references == null ? new ReferenceTable() : references); 055 } 056 057 protected GoloModule self() { return this; } 058 059 public PackageAndClass getPackageAndClass() { 060 return packageAndClass; 061 } 062 063 public PackageAndClass getTypesPackage() { 064 return packageAndClass.createSubPackage(TYPE_SUBPACKAGE); 065 } 066 067 public String sourceFile() { 068 return this.sourceFile == null ? "unknown" : this.sourceFile; 069 } 070 071 public GoloModule sourceFile(String file) { 072 this.sourceFile = file; 073 return this; 074 } 075 076 public ReferenceTable getReferenceTable() { 077 return this.globalReferences; 078 } 079 080 /** 081 * {@inheritDoc} 082 * 083 * @return the module itself. 084 */ 085 @Override 086 public GoloModule enclosingModule() { 087 return this; 088 } 089 090 /** 091 * Returns the module imported by this module, including the implicit ones. 092 */ 093 public Set<ModuleImport> getImports() { 094 Set<ModuleImport> imp = new LinkedHashSet<>(); 095 if (!types.isEmpty()) { 096 imp.add(ModuleImport.implicit(this.getTypesPackage())); 097 for (GoloType<?> t : types) { 098 if (t instanceof Union) { 099 imp.add(ModuleImport.implicit(t.getPackageAndClass())); 100 } 101 } 102 } 103 imp.addAll(imports); 104 if (this.packageAndClass.hasPackage()) { 105 imp.add(ModuleImport.implicit(this.packageAndClass.parentPackage())); 106 } 107 Collections.addAll(imp, DEFAULT_IMPORTS); 108 return imp; 109 } 110 111 /** 112 * Returns the names of the modules used by this module. 113 * 114 * <p>Since the {@code use} macro can inject other dependencies, this list can be not complete. 115 * The macro is not expanded. 116 */ 117 public Set<String> getUsedModules() { 118 Set<String> mods = new LinkedHashSet<>(); 119 for (MacroInvocation m : topLevelMacroInvocations) { 120 if ("use".equals(m.getName())) { 121 Object name = ((ConstantStatement) m.getArguments().get(0)).value(); 122 if (name instanceof String) { mods.add((String) name); } 123 else if (name instanceof ClassReference) { mods.add(((ClassReference) name).getName()); } 124 } 125 } 126 return mods; 127 } 128 129 public Collection<Augmentation> getAugmentations() { 130 return unmodifiableCollection(augmentations.values()); 131 } 132 133 public GoloModule decoratorMacro(MacroInvocation macro) { 134 this.decoratorMacro = macro; 135 return this; 136 } 137 138 public Optional<MacroInvocation> decoratorMacro() { 139 return Optional.ofNullable(this.decoratorMacro); 140 } 141 142 /** 143 * {@inheritDoc} 144 */ 145 @Override 146 public List<GoloFunction> getFunctions() { 147 return new ArrayList<>(functions); 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public boolean hasFunctions() { 155 return !functions.isEmpty(); 156 } 157 158 public boolean hasMacros() { 159 return hasMacros; 160 } 161 162 public boolean hasMain() { 163 return hasMain; 164 } 165 166 /** 167 * {@inheritDoc} 168 */ 169 @Override 170 public void addFunction(GoloFunction function) { 171 function.getBlock().getReferenceTable().relinkTopLevel(globalReferences); 172 functions.add(makeParentOf(function)); 173 if (function.isMain()) { 174 hasMain = true; 175 } 176 if (function.isMacro()) { 177 hasMacros = true; 178 } 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public void addMacroInvocation(MacroInvocation macroCall) { 186 if (macroCall == null) return; 187 this.topLevelMacroInvocations.add(macroCall); 188 makeParentOf(macroCall); 189 } 190 191 private void addAugmentation(Augmentation augment) { 192 PackageAndClass target = augment.getTarget(); 193 if (augmentations.containsKey(target)) { 194 augmentations.get(target).merge(augment); 195 } else { 196 augmentations.put(target, augment); 197 } 198 } 199 200 public GoloElement<?> getSubtypeByName(String name) { 201 if (name == null) { return null; } 202 for (GoloType<?> t : types) { 203 if (name.equals(t.getName())) { 204 return t; 205 } 206 } 207 return null; 208 } 209 210 private void addModuleStateInitializer(AssignmentStatement assignment) { 211 if (!assignment.isDeclaring()) { 212 throw new IllegalArgumentException("Module state must be a declaring assignment"); 213 } 214 assignment.getLocalReference().moduleLevel(); 215 this.moduleState.add(assignment.getLocalReference()); 216 if (moduleStateInitializer == null) { 217 moduleStateInitializer = GoloFunction.function(MODULE_INITIALIZER_FUNCTION) 218 .block(Block.empty().ref(globalReferences)); 219 functions.add(moduleStateInitializer); 220 } 221 moduleStateInitializer.getBlock().add(assignment); 222 } 223 224 public GoloModule add(GoloElement<?> element) { 225 if (element == null || element instanceof Noop) { 226 return this; 227 } 228 if (element instanceof ToplevelElements) { 229 for (GoloElement<?> e : (ToplevelElements) element) { 230 this.add(e); 231 } 232 return this; 233 } 234 makeParentOf(element); 235 if (element instanceof ModuleImport) { 236 imports.add((ModuleImport) element); 237 } else if (element instanceof GoloFunction) { 238 addFunction((GoloFunction) element); 239 } else if (element instanceof GoloType<?>) { 240 GoloType<?> t = (GoloType<?>) element; 241 types.add(t); 242 } else if (element instanceof Augmentation) { 243 addAugmentation((Augmentation) element); 244 } else if (element instanceof NamedAugmentation) { 245 namedAugmentations.add((NamedAugmentation) element); 246 } else if (element instanceof LocalReference) { 247 this.moduleState.add((LocalReference) element); 248 } else if (element instanceof MacroInvocation) { 249 addMacroInvocation((MacroInvocation) element); 250 } else if (element instanceof AssignmentStatement) { 251 addModuleStateInitializer((AssignmentStatement) element); 252 } else { 253 throw new IllegalArgumentException("Can't add a " + element.getClass() + " to a module"); 254 } 255 return this; 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public void accept(GoloIrVisitor visitor) { 263 visitor.visitModule(this); 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 @Override 270 public List<GoloElement<?>> children() { 271 LinkedList<GoloElement<?>> children = new LinkedList<>(); 272 children.addAll(topLevelMacroInvocations); 273 children.addAll(getImports()); 274 children.addAll(types); 275 children.addAll(augmentations.values()); 276 children.addAll(namedAugmentations); 277 children.addAll(moduleState); 278 children.addAll(functions); 279 return children; 280 } 281 282 public boolean isEmpty() { 283 return imports.isEmpty() 284 && types.isEmpty() 285 && augmentations.isEmpty() 286 && namedAugmentations.isEmpty() 287 && moduleState.isEmpty() 288 && functions.isEmpty() 289 && topLevelMacroInvocations.isEmpty(); 290 } 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override 296 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 297 if (original instanceof GoloFunction) { 298 this.functions.remove(original); 299 } else if (original instanceof MacroInvocation) { 300 topLevelMacroInvocations.remove(original); 301 } else { 302 throw cantReplace(original, newElement); 303 } 304 this.add(newElement); 305 } 306 307 @Override 308 public String toString() { 309 return String.format("GoloModule{name=%s, src=%s}", this.packageAndClass, this.sourceFile); 310 } 311}