PageRenderTime 75ms CodeModel.GetById 35ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2-build270707/src/edu/vub/at/objects/natives/NATObject.java

http://ambienttalk.googlecode.com/
Java | 850 lines | 419 code | 99 blank | 332 comment | 55 complexity | bb69e3572c08feba1aeb39653de13158 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * NATObject.java created on Jul 13, 2006 at 3:52:15 PM
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.natives;
 29
 30import edu.vub.at.actors.ATActorMirror;
 31import edu.vub.at.actors.ATAsyncMessage;
 32import edu.vub.at.eval.Evaluator;
 33import edu.vub.at.exceptions.InterpreterException;
 34import edu.vub.at.exceptions.XArityMismatch;
 35import edu.vub.at.exceptions.XDuplicateSlot;
 36import edu.vub.at.exceptions.XSelectorNotFound;
 37import edu.vub.at.exceptions.XTypeMismatch;
 38import edu.vub.at.objects.ATBoolean;
 39import edu.vub.at.objects.ATClosure;
 40import edu.vub.at.objects.ATContext;
 41import edu.vub.at.objects.ATField;
 42import edu.vub.at.objects.ATHandler;
 43import edu.vub.at.objects.ATMessage;
 44import edu.vub.at.objects.ATMethod;
 45import edu.vub.at.objects.ATNil;
 46import edu.vub.at.objects.ATNumber;
 47import edu.vub.at.objects.ATObject;
 48import edu.vub.at.objects.ATTable;
 49import edu.vub.at.objects.ATTypeTag;
 50import edu.vub.at.objects.coercion.Coercer;
 51import edu.vub.at.objects.coercion.NativeTypeTags;
 52import edu.vub.at.objects.grammar.ATBegin;
 53import edu.vub.at.objects.grammar.ATDefinition;
 54import edu.vub.at.objects.grammar.ATMessageCreation;
 55import edu.vub.at.objects.grammar.ATSplice;
 56import edu.vub.at.objects.grammar.ATStatement;
 57import edu.vub.at.objects.grammar.ATSymbol;
 58import edu.vub.at.objects.grammar.ATUnquoteSplice;
 59import edu.vub.at.objects.mirrors.NativeClosure;
 60import edu.vub.at.objects.mirrors.PrimitiveMethod;
 61import edu.vub.at.objects.natives.grammar.AGSplice;
 62import edu.vub.at.objects.natives.grammar.AGSymbol;
 63import edu.vub.at.objects.symbiosis.SymbioticATObjectMarker;
 64import edu.vub.at.util.logging.Logging;
 65
 66import java.io.IOException;
 67import java.util.Collection;
 68import java.util.HashSet;
 69import java.util.Iterator;
 70import java.util.LinkedList;
 71import java.util.Vector;
 72
 73/**
 74 * Native implementation of a default ambienttalk object.
 75 * Although a native AmbientTalk object is implemented as a subtype of callframes,
 76 * the reality is that call frames are a special kind of object.
 77 * This is a pure form of implementation subclassing: we subclass NATCallframe only
 78 * for reusing the field definition/assignment protocol and for inheriting the
 79 * variable map, the state vector and the lexical parent.
 80 * <p>
 81 * NATObjects are one of the five native classes that (almost) fully implement the ATObject interface
 82 * (next to NATCallFrame, NATNil, NATMirage and JavaObject). The implementation is such that
 83 * a NATObject instance represents <b>both</b> a base-level AmbientTalk object, as well as a meta-level
 84 * AmbientTalk mirror on that object.
 85 * 
 86 * An AmbientTalk base-level object has the following structure:
 87 * <ul>
 88 *  <li> properties: a set of boolean flags denoting:
 89 *  <ul>
 90 *   <li> whether the dynamic parent is an IS_A or a SHARES_A parent
 91 *   <li> whether the object shares its variable map with clones
 92 *   <li> whether the object shares its method dictionary with clones
 93 *   <li> whether the object is an isolate (i.e. pass-by-copy)
 94 *  </ul>
 95 *  <li> a variable map, mapping variable names to indices into the state vector
 96 *  <li> a state vector, containing the field values of the object
 97 *  <li> a linked list containing custom field objects
 98 *  <li> a method dictionary, mapping selectors to methods
 99 *  <li> a dynamic object parent, to delegate select and invoke operations
100 *   ( this parent slot is represented by a true AmbientTalk field, rather than by an instance variable )
101 *  <li> a lexical object parent, to support lexical scoping
102 *  <li> a table of type tags that were attached to this object (for classification purposes)
103 * </ul>
104 * 
105 * @author tvcutsem
106 * @author smostinc
107 */
108public class NATObject extends NATCallframe implements ATObject {
109	
110	// The name of the field that points to the dynamic parent
111	public static final AGSymbol _SUPER_NAME_ = AGSymbol.jAlloc("super");
112	
113	// The names of the primitive methods
114	public static final AGSymbol _EQL_NAME_ = AGSymbol.jAlloc("==");
115	public static final AGSymbol _NEW_NAME_ = AGSymbol.jAlloc("new");
116	public static final AGSymbol _INI_NAME_ = AGSymbol.jAlloc("init");
117	
118	// The primitive methods themselves
119	
120	/** def ==(comparand) { nil } */
121	private static final PrimitiveMethod _PRIM_EQL_ = new PrimitiveMethod(
122
123			_EQL_NAME_, NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("comparand")})) {
124      private static final long serialVersionUID = -4475956316807558583L;
125      
126		public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
127			if (!arguments.base_length().equals(NATNumber.ONE)) {
128				throw new XArityMismatch("==", 1, arguments.base_length().asNativeNumber().javaValue);
129			}
130			
131			ATObject comparand = arguments.base_at(NATNumber.ONE);
132			// when comparing against a coercer, skip the coercer
133			if (comparand instanceof SymbioticATObjectMarker) {
134				comparand = ((SymbioticATObjectMarker) comparand)._returnNativeAmbientTalkObject();
135			}
136			
137			// primitive implementation uses pointer equality (as dictated by NativeATObject)
138			return NATBoolean.atValue(ctx.base_lexicalScope() == comparand);
139		}
140	};
141	/** def new(@initargs) { nil } */
142	private static final PrimitiveMethod _PRIM_NEW_ = new PrimitiveMethod(
143			_NEW_NAME_, NATTable.atValue(new ATObject[] { new AGSplice(AGSymbol.jAlloc("initargs")) })) {
144        private static final long serialVersionUID = -5475956316807558583L;
145
146		public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
147			return ctx.base_lexicalScope().base_new(arguments.asNativeTable().elements_);
148		}
149	};
150	/** def init(@initargs) { nil } */
151	private static final PrimitiveMethod _PRIM_INI_ = new PrimitiveMethod(
152			_INI_NAME_, NATTable.atValue(new ATObject[] { new AGSplice(AGSymbol.jAlloc("initargs")) })) {
153	    private static final long serialVersionUID = -6475956316807558583L;
154
155		public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
156			return ctx.base_lexicalScope().asAmbientTalkObject().prim_init(ctx.base_self(), arguments.asNativeTable().elements_);
157		}
158	};
159	
160	/**
161	 * Does the selector signify a 'primitive' method, present in each AmbientTalk object?
162	 */
163	public static boolean isPrimitive(ATSymbol name) {
164		return name.equals(_EQL_NAME_) || name.equals(_NEW_NAME_) || name.equals(_INI_NAME_);
165	}
166	
167	// Auxiliary static methods to support the type of dynamic parent
168	public static final boolean _IS_A_ 		= true;
169	public static final boolean _SHARES_A_ 	= false;
170	
171	/**
172	 * This flag determines the type of parent pointer of this object. We distinguish two cases:
173	 *  - 1: an is-a link, which results in a recursive cloning of the parent when this object is cloned.
174	 *  - 0: a shares-a link, which ensures that clones of this object share the same parent.
175	 */
176	private static final byte _ISAPARENT_FLAG_ = 1<<0;
177	
178	/**
179	 * This flag determines whether or not the field map of this object is shared by other objects:
180	 *  - 1: the map is shared, so modifications must be performed on a copy
181	 *  - 0: the map is not shared, modifications may be directly performed on it
182	 *  
183	 * This flag is important for maintaining the semantics that clones are self-sufficient objects:
184	 * they share field names and methods only at the implementation-level.
185	 */
186	private static final byte _SHARE_MAP_FLAG_ = 1<<1;
187	
188	/**
189	 * Similar to _SHARE_MAP_FLAG__ but for determining the shared status of the method dictionary.
190	 */
191	private static final byte _SHARE_DCT_FLAG_ = 1<<2;
192	
193	/**
194	 * This flag determines whether or not the object is an isolate and hence pass-by-copy:
195	 *  - 1: the object is an isolate, pass-by-copy and no lexical parent except for the root
196	 *  - 0: the object is pass-by-reference and can have any lexical parent
197	 */
198	private static final byte _IS_ISOLATE_FLAG_ = 1<<3;
199
200	/**
201	 * An empty type tag array shared by those objects that do not have any type tags.
202	 */
203	public static final ATTypeTag[] _NO_TYPETAGS_ = new ATTypeTag[0];
204	
205	/**
206	 * The flags of an AmbientTalk object encode the following boolean information:
207	 *  Format: 0b0000idap where
208	 *   p = parent flag: if set, dynamic parent is 'is-a' parent, otherwise 'shares-a' parent
209	 *   a = shares map flag: if set, the map of this object is shared between clones
210	 *   d = shares dictionary flag: if set, the method dictionary of this object is shared between clones
211	 *   i = is isolate flag: if set, the object is passed by copy in inter-actor communication
212	 */
213	private byte flags_;
214	
215	// inherited from NATCallframe:
216	// private FieldMap 	variableMap_;
217	// private Vector	stateVector_;
218	// private LinkedList customFields_;
219	
220	/**
221	 * The method dictionary of this object. It maps method selectors to ATMethod objects.
222	 */
223	private MethodDictionary methodDictionary_;
224	
225	/**
226	 * The types with which this object has been tagged.
227	 */
228	protected ATTypeTag[] typeTags_;
229	
230	/* ------------------
231	 * -- Constructors --
232	 * ------------------ */
233	
234	/**
235	 * Creates an object tagged with the at.types.Isolate type.
236	 * Such an object is called an isolate because:
237	 *  - it has no access to an enclosing lexical scope (except for the root lexical scope)
238	 *  - it can therefore be passed by copy
239	 */
240	public static NATObject createIsolate() {
241		return new NATObject(new ATTypeTag[] { NativeTypeTags._ISOLATE_ });
242	}
243	
244	/**
245	 * Constructs a new AmbientTalk object whose lexical parent is the
246	 * global scope and whose dynamic parent is the dynamic root.
247	 */
248	public NATObject() {
249		this(Evaluator.getGlobalLexicalScope());
250	}
251	
252	/**
253	 * Construct a new AmbientTalk object directly tagged with the given type tags.
254	 */
255	public NATObject(ATTypeTag[] tags) {
256		this(Evaluator.getGlobalLexicalScope(), tags);
257	}
258	
259	/**
260	 * Constructs a new ambienttalk object parametrised by a lexical scope. The 
261	 * object is thus not equipped with a pointer to a dynamic parent.
262	 * @param lexicalParent - the lexical scope in which the object's definition was nested
263	 */
264	public NATObject(ATObject lexicalParent) {
265		this(OBJNil._INSTANCE_, lexicalParent, _SHARES_A_);
266	}
267	
268	/**
269	 * Constructs a new ambienttalk object parametrised by a lexical scope.
270	 * The object's dynamic parent is nil and is tagged with the given table of type tags
271	 */
272	public NATObject(ATObject lexicalParent, ATTypeTag[] tags) {
273		this(OBJNil._INSTANCE_, lexicalParent, _SHARES_A_, tags);
274	}
275
276	/**
277	 * Constructs a new ambienttalk object with the given dynamic parent.
278	 * The lexical parent is assumed to be the global scope.
279	 * @param dynamicParent - the dynamic parent of the new object
280	 * @param parentType - the type of parent link
281	 */
282	public NATObject(ATObject dynamicParent, boolean parentType) {
283		this(dynamicParent, Evaluator.getGlobalLexicalScope(), parentType);
284	}
285
286	/**
287	 * Constructs a new ambienttalk object based on a set of parent pointers.
288	 * The object has no types.
289	 * @param dynamicParent - the parent object of the newly created object
290	 * @param lexicalParent - the lexical scope in which the object's definition was nested
291	 * @param parentType - how this object extends its dynamic parent (is-a or shares-a)
292	 */
293	public NATObject(ATObject dynamicParent, ATObject lexicalParent, boolean parentType) {
294	   this(dynamicParent, lexicalParent, parentType, _NO_TYPETAGS_);
295	}
296	
297	/**
298	 * Constructs a new ambienttalk object based on a set of parent pointers.
299	 * The object is typed with the given types.
300	 * @param dynamicParent - the parent object of the newly created object
301	 * @param lexicalParent - the lexical scope in which the object's definition was nested
302	 * @param parentType - how this object extends its dynamic parent (is-a or shares-a)
303	 * @param tags - the type tags attached to this object
304	 */
305	public NATObject(ATObject dynamicParent, ATObject lexicalParent, boolean parentType, ATTypeTag[] tags) {
306		super(lexicalParent);
307		
308        // by default, an object has a shares-a parent, does not share its map
309		// or dictionary and is no isolate, so all flags are set to 0
310		flags_ = 0;
311		
312		typeTags_ = tags;
313		
314		methodDictionary_ = new MethodDictionary();
315		
316		// bind the dynamic parent to the field named 'super'
317		// we don't pass via meta_defineField as this would trigger mirages too early
318		variableMap_.put(_SUPER_NAME_);
319		stateVector_.add(dynamicParent);
320		
321		// add ==, new and init to the method dictionary directly
322		// we don't pass via meta_addMethod as this would trigger mirages too early
323		methodDictionary_.put(_EQL_NAME_, _PRIM_EQL_);
324		methodDictionary_.put(_NEW_NAME_, _PRIM_NEW_);
325		methodDictionary_.put(_INI_NAME_, _PRIM_INI_);
326		
327		if (parentType) { // parentType == _IS_A_)
328			// requested an 'is-a' parent
329			setFlag(_ISAPARENT_FLAG_); // set is-a parent flag to 1
330		}
331		
332		try {
333            // if this object is tagged as at.types.Isolate, flag it as an isolate
334            // we cannot perform 'this.meta_isTypedAs(ISOLATE)' because this would trigger mirages too early
335			if (isLocallyTaggedAs(NativeTypeTags._ISOLATE_)
336			     || dynamicParent.meta_isTaggedAs(NativeTypeTags._ISOLATE_).asNativeBoolean().javaValue) {
337				setFlag(_IS_ISOLATE_FLAG_);
338				// isolates can only have the global lexical root as their lexical scope
339				lexicalParent_ = Evaluator.getGlobalLexicalScope();
340			}
341		} catch (InterpreterException e) {
342			// some custom type failed to match agains the Isolate type,
343			// the object is not considered an Isolate
344			Logging.Actor_LOG.error("Error testing for Isolate type, ignored:", e);
345		}
346	}
347	
348	/**
349	 * Constructs a new ambienttalk object as a clone of an existing object.
350	 * 
351	 * The caller of this method *must* ensure that the shares flags are set.
352	 * 
353	 * This constructor is responsible for manually re-initialising any custom field
354	 * objects, because the init method of such custom fields is parameterized by the
355	 * clone, which only comes into existence when this constructor runs.
356	 */
357	protected NATObject(FieldMap map,
358			         Vector state,
359			         LinkedList originalCustomFields,
360			         MethodDictionary methodDict,
361			         ATObject dynamicParent,
362			         ATObject lexicalParent,
363			         byte flags,
364			         ATTypeTag[] types) throws InterpreterException {
365		super(map, state, lexicalParent, null);
366		methodDictionary_ = methodDict;
367		
368		flags_ = flags; //a cloned object inherits all flags from original
369		
370		// clone inherits all types (this implies that clones of isolates are also isolates)
371		typeTags_ = types;
372		
373		// ==, new and init should already be present in the method dictionary
374		
375		// set the 'super' field to point to the new dynamic parent
376		setLocalField(_SUPER_NAME_, dynamicParent);
377		
378		// re-initialize all custom fields
379		if (originalCustomFields != null) {
380			customFields_ = new LinkedList();
381			Iterator it = originalCustomFields.iterator();
382			while (it.hasNext()) {
383				ATField field = (ATField) it.next();
384				customFields_.add(field.base_new(new ATObject[] { this }).asField());
385			}
386		}
387	}
388	
389	/**
390	 * Initialize a new AmbientTalk object with the given closure.
391	 * 
392	 * The closure encapsulates:
393	 *  - the code with which to initialize the object
394	 *  - the lexical parent of the object (but that parent should already be set)
395	 *  - the lexically inherited fields for the object (the parameters of the closure)
396	 */
397	public void initializeWithCode(ATClosure code) throws InterpreterException {
398		NATTable copiedBindings = Evaluator.evalMandatoryPars(
399				code.base_method().base_parameters(),
400				code.base_context());
401		code.base_applyInScope(copiedBindings, this);
402	}
403
404	/**
405	 * Invoke NATObject's primitive implementation, such that Java invocations of this
406	 * method have the same behaviour as AmbientTalk invocations.
407	 */
408    public ATObject base_init(ATObject[] initargs) throws InterpreterException {
409    	return this.prim_init(this, initargs);
410    }
411	
412	/**
413	 * The primitive implementation of init in objects is to invoke the init
414	 * method of their parent.
415	 * @param self the object that originally received the 'init' message.
416	 * 
417	 * def init(@args) {
418	 *   super^init(@args)
419	 * }
420	 */
421    private ATObject prim_init(ATObject self, ATObject[] initargs) throws InterpreterException {
422    	return base_super().meta_invoke(self, Evaluator._INIT_, NATTable.atValue(initargs));
423    }
424    
425    public ATBoolean base__opeql__opeql_(ATObject comparand) throws InterpreterException {
426    	return this.meta_invoke(this, _EQL_NAME_, NATTable.of(comparand)).asBoolean();
427    }
428
429	/* ------------------------------------------
430	 * -- Slot accessing and mutating protocol --
431	 * ------------------------------------------ */
432	
433	/**
434	 * When a new field is defined in an object, it is important to check whether or not
435	 * the field map is shared between clones or not. If it is shared, the map must be cloned first.
436	 * @throws InterpreterException 
437	 */
438	public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
439		if (this.isFlagSet(_SHARE_MAP_FLAG_)) {
440			// copy the variable map
441			variableMap_ = variableMap_.copy();
442			// set the 'shares map' flag to false
443			unsetFlag(_SHARE_MAP_FLAG_);
444		}
445		return super.meta_defineField(name, value);
446	}
447	
448	/* ------------------------------------
449	 * -- Extension and cloning protocol --
450	 * ------------------------------------ */
451
452	/**
453	 * When cloning an object, it is first determined whether the parent
454	 * has to be shared by the clone, or whether the parent must also be cloned.
455	 * This depends on whether the dynamic parent is an 'is-a' parent or a 'shares-a'
456	 * parent. This is determined by the _ISAPARENT_FLAG_ object flag.
457	 * 
458	 * A cloned object shares with its original both the variable map
459	 * (to avoid having to copy space for field names) and the method dictionary
460	 * (method bindings are constant and can hence be shared).
461	 * 
462	 * Should either the original or the clone later modify the map or the dictionary
463	 * (at the meta-level), the map or dictionary will be copied first. Hence,
464	 * sharing between clones is an implementation-level optimization: clones
465	 * are completely self-sufficient and do not influence one another by meta-level operations.
466	 */
467	public ATObject meta_clone() throws InterpreterException {
468		ATObject dynamicParent;
469		if(this.isFlagSet(_ISAPARENT_FLAG_)) {
470			// IS-A Relation : clone the dynamic parent.
471			dynamicParent = base_super().meta_clone();
472		} else {
473			// SHARES_A Relation : share the dynamic parent.
474			dynamicParent = base_super();
475		}
476		
477		// ! set the shares flags of this object *and* of its clone
478		// both this object and the clone now share the map and method dictionary
479		setFlag(_SHARE_DCT_FLAG_);
480		setFlag(_SHARE_MAP_FLAG_);
481		
482		NATObject clone = this.createClone(variableMap_,
483				          (Vector) stateVector_.clone(), // shallow copy
484				          customFields_, // must be re-initialized by clone!
485				          methodDictionary_,
486				          dynamicParent,
487				          lexicalParent_,
488				          flags_, typeTags_);
489		
490		return clone;
491	}
492	
493	/**
494	 * When new is invoked on an object's mirror, the object is first cloned
495	 * by the mirror, after which the method named 'init' is invoked on it.
496	 * 
497	 * meta_newInstance(t) = base_init(t) o meta_clone
498	 * 
499	 * Care should be taken that a shares-a child implements its own init method
500	 * which does NOT perform a super-send. If this is not the case, then it is
501	 * possible that a shared parent is accidentally re-initialized because a
502	 * sharing child is cloned via new.
503	 */
504	public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
505		ATObject clone = this.meta_clone();
506		clone.meta_invoke(clone, Evaluator._INIT_, initargs);
507		return clone;
508	}
509
510	public ATBoolean meta_isExtensionOfParent() throws InterpreterException {
511		return NATBoolean.atValue(isFlagSet(_ISAPARENT_FLAG_));
512	}
513	
514	/* ---------------------------------
515	 * -- Structural Access Protocol  --
516	 * --------------------------------- */
517
518	/**
519	 * When a method is added to an object, it is first checked whether the method does not
520	 * already exist. Also, care has to be taken that the method dictionary of an object
521	 * does not affect clones. Therefore, if the method dictionary is shared, a copy
522	 * of the dictionary is taken before adding the method.
523	 * 
524	 * One exception to method addition are primitive methods: if the method added
525	 * would conflict with a primitive method, the primitive is replaced by the new
526	 * method instead.
527	 */
528	public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
529		ATSymbol name = method.base_name();
530		if (methodDictionary_.containsKey(name) && !isPrimitive(name)) {
531			throw new XDuplicateSlot(name);
532		} else {
533			// first check whether the method dictionary is shared
534			if (this.isFlagSet(_SHARE_DCT_FLAG_)) {
535				methodDictionary_ = (MethodDictionary) methodDictionary_.clone();
536				this.unsetFlag(_SHARE_DCT_FLAG_);
537			}
538			methodDictionary_.put(name, method);
539		}
540		return OBJNil._INSTANCE_;
541	}
542
543	public ATMethod meta_grabMethod(ATSymbol selector) throws InterpreterException {
544		ATMethod result = (ATMethod)methodDictionary_.get(selector);
545		if(result == null) {
546			throw new XSelectorNotFound(selector, this);
547		} else {
548			return result;
549		}
550	}
551
552	public ATTable meta_listMethods() throws InterpreterException {
553		Collection methods = methodDictionary_.values();
554		return NATTable.atValue((ATObject[]) methods.toArray(new ATObject[methods.size()]));
555	}
556	
557	public NATText meta_print() throws InterpreterException {
558		if (typeTags_.length == 0) {
559			return NATText.atValue("<object:"+this.hashCode()+">");
560		} else {
561			return NATText.atValue("<object:"+this.hashCode()+
562					               Evaluator.printElements(typeTags_, "[", ",", "]").javaValue+">");
563		}
564	}
565	
566	public boolean isCallFrame() {
567		return false;
568	}
569	
570	/* ---------------------
571	 * -- Mirror Fields   --
572	 * --------------------- */
573	
574	// protected methods, may be adapted by extensions
575	
576	protected NATObject createClone(FieldMap map,
577	         					  Vector state,
578	         					  LinkedList originalCustomFields,
579	         					  MethodDictionary methodDict,
580	         					  ATObject dynamicParent,
581	         					  ATObject lexicalParent,
582	         					  byte flags,
583	         					  ATTypeTag[] types) throws InterpreterException {
584		return new NATObject(map,
585	            state,
586	            originalCustomFields,
587	            methodDict,
588	            dynamicParent,
589	            lexicalParent,
590	            flags,
591	            types);
592	}
593		
594    /* ----------------------------------
595     * -- Object Relational Comparison --
596     * ---------------------------------- */
597    
598	public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
599		if(original instanceof NATObject) {
600			MethodDictionary originalMethods = ((NATObject)original).methodDictionary_;
601			FieldMap originalVariables = ((NATObject)original).variableMap_;
602			
603			return NATBoolean.atValue(
604					methodDictionary_.isDerivedFrom(originalMethods) &
605					variableMap_.isDerivedFrom(originalVariables));
606		} else {
607			return NATBoolean._FALSE_;
608		}
609	}
610
611	public ATBoolean meta_isRelatedTo(final ATObject object) throws InterpreterException {
612		return this.meta_isCloneOf(object).base_or_(
613				new NativeClosure(this) {
614					public ATObject base_apply(ATTable args) throws InterpreterException {
615						return scope_.base_super().meta_isRelatedTo(object);
616					}
617				}).asBoolean();
618	}
619	
620    /* ---------------------------------
621     * -- Type Testing and Querying --
622     * --------------------------------- */
623	
624    /**
625     * Check whether one of the type tags of this object is a subtype of the given type.
626     * If not, then delegate the query to the dynamic parent.
627     */
628    public ATBoolean meta_isTaggedAs(ATTypeTag type) throws InterpreterException {
629    	if (isLocallyTaggedAs(type)) {
630    		return NATBoolean._TRUE_;
631    	} else {
632        	// no type tags match, ask the parent
633        	return base_super().meta_isTaggedAs(type);
634    	}
635    }
636    
637    /**
638     * Return the type tags that were directly attached to this object.
639     */
640    public ATTable meta_typeTags() throws InterpreterException {
641    	// make a copy of the internal type tag array to ensure that the types
642    	// of the object are immutable. Tables allow assignment!
643    	if (typeTags_.length == 0) {
644    		return NATTable.EMPTY;
645    	} else { 
646    		ATTypeTag[] types = new ATTypeTag[typeTags_.length];
647        	System.arraycopy(typeTags_, 0, types, 0, typeTags_.length);
648        	return NATTable.atValue(types);
649    	}
650    }
651    
652    
653	// NATObject has to duplicate the NATByCopy implementation
654	// because NATObject inherits from NATByRef, and because Java has no
655	// multiple inheritance to override that implementation with that of
656    // NATByCopy if this object signifies an isolate.
657	
658    /**
659     * An isolate object does not return a proxy representation of itself
660     * during serialization, hence it is serialized itself. If the object
661     * is not an isolate, invoke the default behaviour for by-reference objects
662     */
663    public ATObject meta_pass() throws InterpreterException {
664    	if (isFlagSet(_IS_ISOLATE_FLAG_)) {
665    		return this;
666    	} else {
667    		return super.meta_pass();
668    	}
669    }
670	
671    /**
672     * An isolate object represents itself upon deserialization.
673     * If this object is not an isolate, the default behaviour for by-reference
674     * objects is invoked.
675     */
676    public ATObject meta_resolve() throws InterpreterException {
677    	if (isFlagSet(_IS_ISOLATE_FLAG_)) {
678    		// re-bind to the new local global lexical root
679    		lexicalParent_ = Evaluator.getGlobalLexicalScope();
680    		return this;
681    	} else {
682    		return super.meta_resolve();
683    	}
684    }
685    
686    /**
687     * This Java serialization hook is overridden merely to provide clearer error messages
688     * in the case of a failing deserialization.
689     */
690	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
691		try {
692			in.defaultReadObject();
693		} catch(ArrayStoreException e) {
694			Logging.Actor_LOG.fatal("Failed to deserialize instance of " + this.getClass(), e);
695			throw new IOException("Object deserialized as wrong type: " + e.getMessage()
696					+ ". Did you forget to make a type tag object an isolate?");
697		}
698	}
699    
700    /* ---------------------------------------
701	 * -- Conversion and Testing Protocol   --
702	 * --------------------------------------- */
703	
704	public NATObject asAmbientTalkObject() { return this; }
705	
706	/**
707	 * ALL asXXX methods return a coercer object which returns a proxy of the correct interface that will 'down'
708	 * subsequent Java base-level invocations to the AmbientTalk level.
709	 * 
710	 * Coercion only happens if the object is tagged with the correct type.
711	 */
712	private Object coerce(ATTypeTag requiredType, Class providedInterface) throws InterpreterException {
713		if (this.meta_isTaggedAs(requiredType).asNativeBoolean().javaValue) {
714			return Coercer.coerce(this, providedInterface);
715		} else {
716			// if the object does not possess the right type tag, raise a type error
717			throw new XTypeMismatch(providedInterface, this);
718		}
719	}
720	
721	public ATBoolean asBoolean() throws InterpreterException { return (ATBoolean) coerce(NativeTypeTags._BOOLEAN_, ATBoolean.class); }
722	public ATClosure asClosure() throws InterpreterException { return (ATClosure) coerce(NativeTypeTags._CLOSURE_, ATClosure.class); }
723	public ATField asField() throws InterpreterException { return (ATField) coerce(NativeTypeTags._FIELD_, ATField.class); }
724	public ATMessage asMessage() throws InterpreterException { return (ATMessage) coerce(NativeTypeTags._MESSAGE_, ATMessage.class); }
725	public ATMethod asMethod() throws InterpreterException { return (ATMethod) coerce(NativeTypeTags._METHOD_, ATMethod.class); }
726	public ATHandler asHandler() throws InterpreterException { return (ATHandler) coerce(NativeTypeTags._HANDLER_, ATHandler.class); }
727	public ATNumber asNumber() throws InterpreterException { return (ATNumber) coerce(NativeTypeTags._NUMBER_, ATNumber.class); }
728	public ATTable asTable() throws InterpreterException { return (ATTable) coerce(NativeTypeTags._TABLE_, ATTable.class); }
729    public ATAsyncMessage asAsyncMessage() throws InterpreterException { return (ATAsyncMessage) coerce(NativeTypeTags._ASYNCMSG_, ATAsyncMessage.class);}
730    public ATActorMirror asActorMirror() throws InterpreterException { return (ATActorMirror) coerce(NativeTypeTags._ACTORMIRROR_, ATActorMirror.class); }
731    public ATTypeTag asTypeTag() throws InterpreterException { return (ATTypeTag) coerce(NativeTypeTags._TYPETAG_, ATTypeTag.class); }
732	
733	public ATBegin asBegin() throws InterpreterException { return (ATBegin) coerce(NativeTypeTags._BEGIN_, ATBegin.class); }
734	public ATStatement asStatement() throws InterpreterException { return (ATStatement) coerce(NativeTypeTags._STATEMENT_, ATStatement.class); }
735    public ATUnquoteSplice asUnquoteSplice() throws InterpreterException { return (ATUnquoteSplice) coerce(NativeTypeTags._UQSPLICE_, ATUnquoteSplice.class); }
736    public ATSymbol asSymbol() throws InterpreterException { return (ATSymbol) coerce(NativeTypeTags._SYMBOL_, ATSymbol.class); }
737    public ATSplice asSplice() throws InterpreterException { return (ATSplice) coerce(NativeTypeTags._SPLICE_, ATSplice.class); }
738	public ATDefinition asDefinition() throws InterpreterException { return (ATDefinition) coerce(NativeTypeTags._DEFINITION_, ATDefinition.class); }
739	public ATMessageCreation asMessageCreation() throws InterpreterException { return (ATMessageCreation) coerce(NativeTypeTags._MSGCREATION_, ATMessageCreation.class); }
740	
741	// ALL isXXX methods return true (can be overridden by programmer-defined base-level methods)
742	
743	public boolean isAmbientTalkObject() { return true; }
744	
745	// objects can only be 'cast' to a native category if they are marked with
746	// the appropriate native type
747	public boolean isSplice() throws InterpreterException { return meta_isTaggedAs(NativeTypeTags._SPLICE_).asNativeBoolean().javaValue; }
748	public boolean isSymbol() throws InterpreterException { return meta_isTaggedAs(NativeTypeTags._SYMBOL_).asNativeBoolean().javaValue; }
749	public boolean isTable() throws InterpreterException { return meta_isTaggedAs(NativeTypeTags._TABLE_).asNativeBoolean().javaValue; }
750	public boolean isUnquoteSplice() throws InterpreterException { return meta_isTaggedAs(NativeTypeTags._UQSPLICE_).asNativeBoolean().javaValue; }
751	public boolean isTypeTag() throws InterpreterException { return meta_isTaggedAs(NativeTypeTags._TYPETAG_).asNativeBoolean().javaValue; }
752	
753	
754	// private methods
755	
756	private boolean isFlagSet(byte flag) {
757		return (flags_ & flag) != 0;
758	}
759
760	private void setFlag(byte flag) {
761		flags_ = (byte) (flags_ | flag);
762	}
763
764	private void unsetFlag(byte flag) {
765		flags_ = (byte) (flags_ & (~flag));
766	}
767	
768	protected boolean hasLocalMethod(ATSymbol selector) throws InterpreterException {
769		return methodDictionary_.containsKey(selector);
770	}
771	
772	protected ATMethod getLocalMethod(ATSymbol selector) throws InterpreterException {
773		ATMethod result = ((ATObject) methodDictionary_.get(selector)).asMethod();
774		if(result == null) {
775			throw new XSelectorNotFound(selector, this);
776		} else {
777			return result;
778		}
779	}
780	
781	/**
782	 * Performs a type test for this object locally.
783	 * @return whether this object is tagged with a particular type tag or not.
784	 */
785	private boolean isLocallyTaggedAs(ATTypeTag tag) throws InterpreterException {
786    	for (int i = 0; i < typeTags_.length; i++) {
787			if (typeTags_[i].base_isSubtypeOf(tag).asNativeBoolean().javaValue) {
788				// if one type matches, return true
789				return true;
790			}
791		}
792		return false;
793	}
794	
795	/**
796	 * Auxiliary method to access the fields of an object and all of its super-objects up to (but excluding) nil.
797	 * Overridden fields of parent objects are not included.
798	 */
799	public static ATField[] listTransitiveFields(ATObject obj) throws InterpreterException {
800		Vector fields = new Vector();
801		HashSet encounteredNames = new HashSet(); // to filter duplicates
802		for (; obj != OBJNil._INSTANCE_ ; obj = obj.base_super()) {
803			ATObject[] localFields = obj.meta_listFields().asNativeTable().elements_;
804			for (int i = 0; i < localFields.length; i++) {
805				ATField field = localFields[i].asField();
806				ATSymbol fieldName = field.base_name();
807				if (!encounteredNames.contains(fieldName)) {
808					fields.add(field);
809					encounteredNames.add(fieldName);
810				}
811			}
812		}
813		return (ATField[]) fields.toArray(new ATField[fields.size()]);
814	}
815	
816	/**
817	 * Auxiliary method to access the methods of an object and all of its super-objects up to (but excluding) nil.
818	 * Overridden methods of parent objects are not included.
819	 */
820	public static ATMethod[] listTransitiveMethods(ATObject obj) throws InterpreterException {
821		Vector methods = new Vector();
822		HashSet encounteredNames = new HashSet(); // to filter duplicates		
823		for (; obj != OBJNil._INSTANCE_ ; obj = obj.base_super()) {
824			// fast-path for native objects
825			if (obj instanceof NATObject) {
826				Collection localMethods = ((NATObject) obj).methodDictionary_.values();
827				for (Iterator iter = localMethods.iterator(); iter.hasNext();) {
828					ATMethod localMethod = (ATMethod) iter.next();
829					ATSymbol methodName = localMethod.base_name();
830					if (!encounteredNames.contains(methodName)) {
831						methods.add(localMethod);
832						encounteredNames.add(methodName);
833					}
834				}
835			} else {
836				ATObject[] localMethods = obj.meta_listMethods().asNativeTable().elements_;
837				for (int i = 0; i < localMethods.length; i++) {
838					ATMethod localMethod = localMethods[i].asMethod();
839					ATSymbol methodName = localMethod.base_name();
840					if (!encounteredNames.contains(methodName)) {
841						methods.add(localMethod);
842						encounteredNames.add(methodName);
843					}
844				}
845			}
846		}
847		return (ATMethod[]) methods.toArray(new ATMethod[methods.size()]);
848	}
849	
850}