/******************************************************************************* * * Copyright (C) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.interpreter.values; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.Vector; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.assistant.pattern.PTypeList; import org.overture.ast.definitions.ASystemClassDefinition; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.lex.LexNameList; import org.overture.ast.messages.InternalException; import org.overture.ast.types.AClassType; import org.overture.ast.types.PType; import org.overture.ast.util.Utils; import org.overture.interpreter.runtime.Context; import org.overture.interpreter.runtime.Interpreter; import org.overture.interpreter.runtime.ObjectContext; import org.overture.interpreter.runtime.ValueException; import org.overture.interpreter.scheduler.Lock; import org.overture.typechecker.util.HackLexNameToken; public class ObjectValue extends Value { private static final long serialVersionUID = 1L; private static int nextObjectReference = 0; public final int objectReference; public final AClassType type; public final NameValuePairMap members; public final List<ObjectValue> superobjects; /** * The list holds all object values created by this object value */ public final List<ObjectValue> children; public ClassInvariantListener invlistener = null; public transient Lock guardLock; private transient CPUValue CPU; private Object delegateObject = null; private int periodicCount = 0; private int periodicOverlaps = 0; /** * The Object value who created this instance */ public ObjectValue creator; public ObjectValue(AClassType type, NameValuePairMap members, List<ObjectValue> superobjects, CPUValue cpu, ObjectValue creator) { this.objectReference = getReference(); this.type = type; this.members = members; this.superobjects = superobjects; this.CPU = cpu; this.guardLock = new Lock(); this.children = new LinkedList<ObjectValue>(); if (creator != null) { setCreator(creator); } setSelf(this); } private static synchronized int getReference() { return ++nextObjectReference; } private void setSelf(ObjectValue self) { for (NameValuePair nvp : members.asList()) { Value deref = nvp.value.deref(); if (deref instanceof OperationValue) { OperationValue ov = (OperationValue) deref; ov.setSelf(self); } else if (deref instanceof FunctionValue) { FunctionValue fv = (FunctionValue) deref; fv.setSelf(self); } else if (deref instanceof ObjectValue) { ((ObjectValue) deref).setCreator(self); } } for (ObjectValue obj : superobjects) { obj.setSelf(self); } } @Override public ObjectValue objectValue(Context ctxt) { return this; } public PType getType() { return type; } public PTypeList getBaseTypes() { PTypeList basetypes = new PTypeList(); if (superobjects.isEmpty()) { basetypes.add(type); } else { for (ObjectValue sup : superobjects) { basetypes.addAll(sup.getBaseTypes()); } } return basetypes; } @Override public Value getUpdatable(ValueListenerList listeners) { for (Entry<ILexNameToken, Value> m: members.entrySet()) { Value v = m.getValue(); if (v instanceof UpdatableValue && listeners != null) { // Update one level of listeners in-place (see UpdatableValue) UpdatableValue uv = (UpdatableValue)v; uv.addListeners(listeners); // Concurrent update with listener invocations } } // for (Entry<ILexNameToken, Value> m: members.entrySet()) // { // Value v = m.getValue(); // // if (v.deref() instanceof ObjectValue) // { // // Don't recurse into inner objects, just mark field itself // m.setValue(UpdatableValue.factory(v, listeners)); // } // else if (v.deref() instanceof FunctionValue) // { // // Ignore function members // } // else if (v.deref() instanceof OperationValue) // { // // Ignore operation members // } // else // { // m.setValue(v.getUpdatable(listeners)); // } // } return UpdatableValue.factory(this, listeners); } public OperationValue getThreadOperation(Context ctxt) throws ValueException { return get(type.getClassdef().getName().getThreadName(), false).operationValue(ctxt); } public synchronized int incPeriodicCount() { if (periodicCount > 0) { periodicOverlaps++; } periodicCount++; return periodicOverlaps; } public synchronized void decPeriodicCount() { periodicCount--; } public synchronized Value get(ILexNameToken field, boolean explicit) { ILexNameToken localname = explicit ? field : field.getModifiedName(type.getName().getName()); // This is another case where we have to iterate with equals() // rather than using the map's hash, because the hash doesn't // take account of the TypeComparator looseness when comparing // qualified names. Not very efficient... so we try a raw get // first. Value rv = members.get(localname); if (rv == null) { for (ILexNameToken var : members.keySet()) { if (HackLexNameToken.isEqual(var, localname)) { rv = members.get(var); break; } } } if (rv != null) { return rv; } for (ObjectValue svalue : superobjects) { rv = svalue.get(field, explicit); if (rv != null) { return rv; } } return null; } public ValueList getOverloads(ILexNameToken field) { ValueList list = new ValueList(); // This is another case where we have to iterate with matches() // rather than using the map's hash, because the hash includes the // overloaded type qualifiers... for (ILexNameToken var : members.keySet()) { if (var.matches(field)) // Ignore type qualifiers { list.add(members.get(var)); } } if (!list.isEmpty()) { return list; // Only names from one level } for (ObjectValue svalue : superobjects) { list = svalue.getOverloads(field); if (!list.isEmpty()) { return list; } } return list; } public NameValuePairMap getMemberValues() { NameValuePairMap nvpm = new NameValuePairMap(); for (ObjectValue svalue : superobjects) { nvpm.putAll(svalue.getMemberValues()); } nvpm.putAll(members); return nvpm; } @Override public boolean equals(Object other) { if (other instanceof Value) { Value val = ((Value) other).deref(); if (val instanceof ObjectValue) { return ((ObjectValue) val).objectReference == this.objectReference; } } return false; } private boolean inToString = false; @Override public String toString() { if (inToString) { return "{#" + objectReference + " recursive}"; } inToString = true; StringBuilder sb = new StringBuilder(); sb.append(type.toString()); sb.append("{#" + objectReference); for (ILexNameToken name : members.keySet()) { Value ov = members.get(name); Value v = ov.deref(); if (!(v instanceof FunctionValue) && !(v instanceof OperationValue)) { sb.append(", "); sb.append(name.getName()); if (ov instanceof UpdatableValue) { sb.append(":="); } else { sb.append("="); } sb.append(v.toString()); } } if (!superobjects.isEmpty()) { sb.append(", "); sb.append(Utils.listToString(superobjects)); } sb.append("}"); inToString = false; return sb.toString(); } @Override public int hashCode() { // return type.hashCode() + objectReference + superobjects.hashCode(); return objectReference; } @Override public String kind() { return type.toString(); } @Override protected Value convertValueTo(PType to, Context ctxt, Set<PType> done) throws AnalysisException { Value conv = convertToHierarchy(to); if (conv != null) { return conv; } // This will fail... return super.convertValueTo(to, ctxt, done); } private Value convertToHierarchy(PType to) { if (to.equals(type)) { return this; } for (ObjectValue svalue : superobjects) { Value conv = svalue.convertToHierarchy(to); if (conv != null) { return this; // NB. not the subtype } } return null; } @Override public Object clone() { return deepCopy(); } private ObjectValue mycopy = null; @Override public ObjectValue shallowCopy() { if (mycopy != null) { return mycopy; } mycopy = new ObjectValue(type, new NameValuePairMap(), new Vector<ObjectValue>(), CPU, creator); List<ObjectValue> supers = mycopy.superobjects; NameValuePairMap memcopy = mycopy.members; for (ObjectValue sobj : superobjects) { supers.add( // Type skeleton only... new ObjectValue(sobj.type, new NameValuePairMap(), new Vector<ObjectValue>(), sobj.CPU, creator)); } for (ILexNameToken name : members.keySet()) { Value mv = members.get(name); if (mv.deref() instanceof ObjectValue) { ObjectValue om = (ObjectValue) mv.deref(); memcopy.put(name, om.shallowCopy()); } else { memcopy.put(name, (Value) mv.clone()); } } mycopy.setSelf(mycopy); ObjectValue rv = mycopy; mycopy = null; return rv; } @Override public ObjectValue deepCopy() { try { // This is slow, but it has the advantage that Value copies, // such as the parent and subclass copies of the same // variable, are preserved as the same variable rather than // being split, as they are in naive object copies. ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(this); oos.close(); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); ObjectInputStream ois = new ObjectInputStream(is); ObjectValue result = (ObjectValue) ois.readObject(); result.setSelf(result); return result; } catch (Exception e) { throw new InternalException(5, "Illegal clone: " + e); } } public MapValue getOldValues(LexNameList oldnames) { ValueMap values = new ValueMap(); ObjectContext ctxt = new ObjectContext(Interpreter.getInstance().getAssistantFactory(), type.getLocation(), "Old Object Creation", null, this); for (ILexNameToken name : oldnames) { Value mv = ctxt.check(name.getNewName()).deref(); SeqValue sname = new SeqValue(name.getName()); if (mv instanceof ObjectValue) { ObjectValue om = (ObjectValue) mv; values.put(sname, om.deepCopy()); } else { values.put(sname, (Value) mv.clone()); } } return new MapValue(values); } // private void writeObject(ObjectOutputStream out) // throws IOException // { // out.defaultWriteObject(); // } private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); CPU = CPUValue.vCPU; guardLock = new Lock(); } public synchronized void setCPU(CPUValue cpu) { CPU = cpu; } public synchronized CPUValue getCPU() { return CPU == null ? CPUValue.vCPU : CPU; } public boolean hasDelegate(Context ctxt) { if (ctxt.assistantFactory.createSClassDefinitionAssistant().hasDelegate(type.getClassdef())) { if (delegateObject == null) { delegateObject = ctxt.assistantFactory.createSClassDefinitionAssistant().newInstance(type.getClassdef()); } return true; } return false; } public Value invokeDelegate(Context ctxt) { return ctxt.assistantFactory.createSClassDefinitionAssistant().invokeDelegate(type.getClassdef(), delegateObject, ctxt); } public static void init() { nextObjectReference = 0; } public void setListener(ClassInvariantListener listener) { invlistener = listener; listener.invopvalue.setSelf(this); } /** * Sets the creator of this object value and adds this to the newCreator parsed as argument * * @param newCreator * The creator of this object value */ private synchronized void setCreator(ObjectValue newCreator) { // Do not set the creator if created by the system class. The System contains // fields with references to Thread instances which are not Serializable and // will fail a deep copy with a NotSerializableExpection for Thread if (newCreator != null && newCreator.type.getClassdef() instanceof ASystemClassDefinition) { return; } // establish transitive reference newCreator.addChild(this); } /** * Removed the creator of this object value by detaching from the creator's child list */ public synchronized void removeCreator() { // if we are moving to a new CPU, we are no longer a part of the transitive // references from our creator, so let us remove ourself. This will prevent // us from being updated if our creator is migrating in the // future. if (this.creator != null) { this.creator.removeChild(this); // creator no longer needed, as we already detached ourself. this.creator = null; } } /** * Add a child created by this object value * * @param referenced */ private synchronized void addChild(ObjectValue referenced) { children.add(referenced); } /** * Remove a child from this object value. After this the reference will no longer be considered as created by this * object value * * @param reference */ private synchronized void removeChild(ObjectValue reference) { children.remove(reference); } }