package org.cx4a.rsense.typing.vertex; import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.util.Collections; import org.jrubyparser.ast.Node; import org.jrubyparser.ast.FixnumNode; import org.jrubyparser.ast.StrNode; import org.jrubyparser.ast.SymbolNode; import org.jrubyparser.ast.INameNode; import org.cx4a.rsense.ruby.IRubyObject; import org.cx4a.rsense.typing.TypeSet; import org.cx4a.rsense.typing.Propagation; import org.cx4a.rsense.typing.runtime.PolymorphicObject; import org.cx4a.rsense.util.Logger; public class Vertex { public static final int MEGAMORPHIC_THRESHOLD = 5; public static final Vertex EMPTY = new Vertex(null, TypeSet.EMPTY) { @Override public void addEdge(Vertex dest) {} @Override public void removeEdge(Vertex dest) {} }; static { EMPTY.edges = Collections.emptyList(); } protected Node node; protected int capacity = MEGAMORPHIC_THRESHOLD + 1; protected TypeSet typeSet; protected IRubyObject singleType; protected boolean megamorphic; protected List edges; protected boolean changed = true; protected long changedTime; public Vertex() {} public Vertex(int capacity) { this.capacity = capacity; } public Vertex(Node node) { this.node = node; } public Vertex(Node node, int capacity) { this.node = node; this.capacity = capacity; } public Vertex(Node node, TypeSet typeSet) { this.node = node; this.typeSet = typeSet; } public Node getNode() { return node; } public TypeSet getTypeSet() { return typeSet != null ? typeSet : TypeSet.EMPTY; } public List getEdges() { return edges != null ? edges : Collections.emptyList(); } public boolean isEmpty() { return typeSet == null || typeSet.isEmpty(); } public boolean addType(IRubyObject type) { if (type == null) throw new NullPointerException("type is null"); if (megamorphic) return false; if (typeSet().size() >= MEGAMORPHIC_THRESHOLD) { Logger.info("megamorphic detected"); megamorphic = true; // FIXME generalize singleType = type.getRuntime().newInstance(type.getRuntime().getObject()); typeSet = new TypeSet(1); typeSet.add(singleType); return false; } else { if (singleType == null) singleType = type; if (typeSet().add(type)) return changed = true; else return false; } } public boolean addTypes(Collection types) { boolean added = false; for (IRubyObject type : types) { if (addType(type)) added = true; } return added; } public boolean update(Vertex other) { return addTypes(other.getTypeSet()); } public boolean isChanged() { if (changed) return true; // Check to see if polymorphic objects are changed if (typeSet != null && !megamorphic) { for (IRubyObject obj : typeSet) { if (obj instanceof PolymorphicObject) { PolymorphicObject pobj = (PolymorphicObject) obj; if (changedTime <= pobj.getModifiedTime()) return changed = true; } } } return false; } public void markUnchanged() { if (changed) changedTime = System.currentTimeMillis(); changed = false; } public IRubyObject singleType() { if (typeSet == null || typeSet.size() != 1) return null; else if (singleType != null) return singleType; else return singleType = typeSet.iterator().next(); } public void addEdge(Vertex dest) { if (edges == null) edges = new ArrayList(2); edges.add(dest); } public void removeEdge(Vertex dest) { if (edges != null) edges.remove(dest); } public boolean accept(Propagation propagation, Vertex src) { return propagation.getGraph().propagateVertex(propagation, this, src); } public boolean isFree() { return node == null; } @Override public String toString() { return getTypeSet().toString(); } private TypeSet typeSet() { if (typeSet == null) typeSet = new TypeSet(capacity); return typeSet; } public static String getName(Vertex vertex) { Node node = vertex.getNode(); if (node instanceof INameNode) { return ((INameNode) node).getName(); } else { return null; } } public static Integer getFixnum(Vertex vertex) { Node node = vertex.getNode(); if (node instanceof FixnumNode) { return Integer.valueOf((int) ((FixnumNode) node).getValue()); } else { return null; } } public static String getString(Vertex vertex) { Node node = vertex.getNode(); if (node instanceof StrNode) { return ((StrNode) node).getValue().toString(); } else { return null; } } public static String getSymbol(Vertex vertex) { Node node = vertex.getNode(); if (node instanceof SymbolNode) { return ((SymbolNode) node).getName(); } else { return null; } } public static String getStringOrSymbol(Vertex vertex) { String value = getString(vertex); return value != null ? value : getSymbol(vertex); } }