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 java.util.*; 014 015import static java.util.Collections.unmodifiableCollection; 016import static java.util.Collections.unmodifiableSet; 017 018public final class ReferenceTable { 019 020 private ReferenceTable parent; 021 private final Map<String, LocalReference> table = new LinkedHashMap<>(); 022 023 public ReferenceTable() { 024 this(null); 025 } 026 027 private ReferenceTable(ReferenceTable parent) { 028 this.parent = parent; 029 } 030 031 public ReferenceTable parent() { 032 return this.parent; 033 } 034 035 public boolean isEmpty() { 036 return this.table.isEmpty(); 037 } 038 039 public ReferenceTable add(LocalReference reference) { 040 table.put(reference.getName(), reference); 041 return this; 042 } 043 044 public int size() { 045 return table.size() + (parent != null ? parent.size() : 0); 046 } 047 048 public boolean hasReferenceFor(String name) { 049 return table.containsKey(name) || parent != null && parent.hasReferenceFor(name); 050 } 051 052 public void updateFrom(GoloStatement<?> statement) { 053 if (statement instanceof ReferencesHolder) { 054 for (LocalReference r : ((ReferencesHolder) statement).getDeclaringReferences()) { 055 this.add(r); 056 } 057 } 058 } 059 060 public LocalReference get(String name) { 061 LocalReference reference = table.get(name); 062 if (reference != null) { 063 return reference; 064 } 065 if (parent != null && parent != this) { 066 return parent.get(name); 067 } 068 return null; 069 } 070 071 public Set<String> ownedSymbols() { 072 return unmodifiableSet(table.keySet()); 073 } 074 075 public Collection<LocalReference> ownedReferences() { 076 return unmodifiableCollection(table.values()); 077 } 078 079 /** 080 * Redefines the parent table for this table. 081 * 082 * <p>If {@code prune} is {@code true}, the owned references already presents in the parent are removed. 083 * 084 * @param parent the new parent table 085 * @param prune remove duplicated owned references 086 */ 087 public void relink(ReferenceTable parent, boolean prune) { 088 if (parent == this) { return; } 089 if (prune) { 090 for (LocalReference reference : parent.references()) { 091 if (this.table.containsKey(reference.getName())) { 092 this.remove(reference.getName()); 093 } 094 } 095 } 096 this.parent = parent; 097 } 098 099 public void relink(ReferenceTable parent) { 100 relink(parent, true); 101 } 102 103 private boolean isLinkedTo(ReferenceTable other) { 104 return this == other 105 || this.parent == other 106 || (this.parent != null && this.parent.isLinkedTo(other)); 107 } 108 109 public void relinkTopLevel(ReferenceTable topLevel) { 110 if (this == topLevel) { return; } 111 if (this.parent == null) { 112 this.parent = topLevel; 113 } else if (!this.isLinkedTo(topLevel) && !topLevel.isLinkedTo(this)) { 114 this.parent.relinkTopLevel(topLevel); 115 } 116 } 117 118 public Set<String> symbols() { 119 LinkedHashSet<String> localSymbols = new LinkedHashSet<>(table.keySet()); 120 if (parent != null) { 121 localSymbols.addAll(parent.symbols()); 122 } 123 return localSymbols; 124 } 125 126 /** 127 * Return the set of the references known to this table. 128 * <p> 129 * Contains the own references as well as the ones from the parent table. 130 */ 131 public Collection<LocalReference> references() { 132 Collection<LocalReference> localReferences = new LinkedHashSet<>(table.values()); 133 if (parent != null) { 134 for (LocalReference ref : parent.references()) { 135 if (!table.containsKey(ref.getName())) { 136 localReferences.add(ref); 137 } 138 } 139 } 140 return localReferences; 141 } 142 143 public ReferenceTable fork() { 144 return new ReferenceTable(this); 145 } 146 147 public ReferenceTable flatDeepCopy(boolean turnIntoConstants) { 148 ReferenceTable referenceTable = new ReferenceTable(); 149 for (LocalReference reference : references()) { 150 String refName = reference.getName(); 151 if (reference.isModuleState()) { 152 referenceTable.add(LocalReference.of(refName) 153 .kind(reference.getKind())); 154 continue; 155 } 156 if (turnIntoConstants && !table.containsKey(refName)) { 157 referenceTable.add(LocalReference.of(refName) 158 .synthetic(reference.isSynthetic())); 159 } else { 160 referenceTable.add(LocalReference.of(refName) 161 .synthetic(reference.isSynthetic()) 162 .kind(reference.getKind())); 163 } 164 } 165 return referenceTable; 166 } 167 168 public void remove(String name) { 169 table.remove(name); 170 } 171 172 @Override 173 public String toString() { 174 StringBuilder representation = new StringBuilder("ReferenceTable: {\n"); 175 for (Map.Entry<String, LocalReference> elt : table.entrySet()) { 176 representation.append(elt.getKey()).append(": ").append(elt.getValue()).append('\n'); 177 } 178 representation.append('}'); 179 if (parent != null && parent != this) { 180 representation.append(" => ").append(parent.toString()); 181 } 182 return representation.toString(); 183 } 184}