/interpreter/tags/at2dist041108/src/edu/vub/at/objects/natives/NATObject.java

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