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

http://ambienttalk.googlecode.com/ · Java · 946 lines · 444 code · 98 blank · 404 comment · 69 complexity · af59d6691c9622dc16d8c804aad163cf 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.net.Logging;
  32. import edu.vub.at.eval.Evaluator;
  33. import edu.vub.at.exceptions.InterpreterException;
  34. import edu.vub.at.exceptions.XArityMismatch;
  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.objects.ATBoolean;
  39. import edu.vub.at.objects.ATClosure;
  40. import edu.vub.at.objects.ATContext;
  41. import edu.vub.at.objects.ATField;
  42. import edu.vub.at.objects.ATHandler;
  43. import edu.vub.at.objects.ATMessage;
  44. import edu.vub.at.objects.ATMethod;
  45. import edu.vub.at.objects.ATNil;
  46. import edu.vub.at.objects.ATNumber;
  47. import edu.vub.at.objects.ATObject;
  48. import edu.vub.at.objects.ATStripe;
  49. import edu.vub.at.objects.ATTable;
  50. import edu.vub.at.objects.coercion.Coercer;
  51. import edu.vub.at.objects.coercion.NativeStripes;
  52. import edu.vub.at.objects.grammar.ATBegin;
  53. import edu.vub.at.objects.grammar.ATDefinition;
  54. import edu.vub.at.objects.grammar.ATExpression;
  55. import edu.vub.at.objects.grammar.ATMessageCreation;
  56. import edu.vub.at.objects.grammar.ATSplice;
  57. import edu.vub.at.objects.grammar.ATStatement;
  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.mirrors.PrimitiveMethod;
  62. import edu.vub.at.objects.natives.grammar.AGSplice;
  63. import edu.vub.at.objects.natives.grammar.AGSymbol;
  64. import java.util.Collection;
  65. import java.util.HashSet;
  66. import java.util.Iterator;
  67. import java.util.LinkedList;
  68. import java.util.Vector;
  69. /**
  70. * Native implementation of a default ambienttalk object.
  71. *
  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. *
  75. * This is a pure form of implementation subclassing: we subclass NATCallframe only
  76. * for reusing the field definition/assignment protocol and for inheriting the
  77. * variable map, the state vector and the lexical parent.
  78. *
  79. * NATObjects are one of the five native classes that fully implement the ATObject interface
  80. * (next to NATCallFrame, NATNil, NATMirage and NATSuperObject). The implementation is such that
  81. * a NATObject instance represents *both* a base-level AmbientTalk object, as well as a meta-level
  82. * AmbientTalk mirror on that object.
  83. *
  84. * An AmbientTalk base-level object has the following structure:
  85. * - properties: a set of boolean flags denoting:
  86. * - whether the dynamic parent is an IS_A or a SHARES_A parent
  87. * - whether the object shares its variable map with clones
  88. * - whether the object shares its method dictionary with clones
  89. * - whether the object is an isolate (i.e. pass-by-copy)
  90. * - whether the object is a mirror (a meta-level object of a corresponding base-level object)
  91. * - a variable map, mapping variable names to indices into the state vector
  92. * - a state vector, containing the field values of the object
  93. * - a linked list containing custom field objects
  94. * - a method dictionary, mapping selectors to methods
  95. * - a dynamic object parent, to delegate select and invoke operations
  96. * ( this parent slot is represented by a true AmbientTalk field, rather than by an instance variable )
  97. * - a lexical object parent, to support lexical scoping
  98. * - a table of stripes that were attached to this object (for classification purposes)
  99. *
  100. * @author tvcutsem
  101. * @author smostinc
  102. */
  103. public class NATObject extends NATCallframe implements ATObject {
  104. // The name of the field that points to the dynamic parent
  105. public static final AGSymbol _SUPER_NAME_ = AGSymbol.jAlloc("super");
  106. // The names of the primitive methods
  107. public static final AGSymbol _EQL_NAME_ = AGSymbol.jAlloc("==");
  108. public static final AGSymbol _NEW_NAME_ = AGSymbol.jAlloc("new");
  109. public static final AGSymbol _INI_NAME_ = AGSymbol.jAlloc("init");
  110. // The primitive methods themselves
  111. /** def ==(comparand) { nil } */
  112. private static final PrimitiveMethod _PRIM_EQL_ = new PrimitiveMethod(
  113. _EQL_NAME_, NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("comparand")})) {
  114. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  115. if (!arguments.base_getLength().equals(NATNumber.ONE)) {
  116. throw new XArityMismatch("==", 1, arguments.base_getLength().asNativeNumber().javaValue);
  117. }
  118. return ctx.base_getLexicalScope().base__opeql__opeql_(arguments.base_at(NATNumber.ONE));
  119. }
  120. };
  121. /** def new(@initargs) { nil } */
  122. private static final PrimitiveMethod _PRIM_NEW_ = new PrimitiveMethod(
  123. _NEW_NAME_, NATTable.atValue(new ATObject[] { new AGSplice(AGSymbol.jAlloc("initargs")) })) {
  124. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  125. return ctx.base_getLexicalScope().base_new(arguments.asNativeTable().elements_);
  126. }
  127. };
  128. /** def init(@initargs) { nil } */
  129. private static final PrimitiveMethod _PRIM_INI_ = new PrimitiveMethod(
  130. _INI_NAME_, NATTable.atValue(new ATObject[] { new AGSplice(AGSymbol.jAlloc("initargs")) })) {
  131. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  132. return ctx.base_getLexicalScope().asAmbientTalkObject().prim_init(ctx.base_getSelf(), arguments.asNativeTable().elements_);
  133. }
  134. };
  135. /**
  136. * Does the selector signify a 'primitive' method, present in each AmbientTalk object?
  137. */
  138. public static boolean isPrimitive(ATSymbol name) {
  139. return name.equals(_EQL_NAME_) || name.equals(_NEW_NAME_) || name.equals(_INI_NAME_);
  140. }
  141. // Auxiliary static methods to support the type of dynamic parent
  142. public static final boolean _IS_A_ = true;
  143. public static final boolean _SHARES_A_ = false;
  144. /**
  145. * This flag determines the type of parent pointer of this object. We distinguish two cases:
  146. * - 1: an is-a link, which results in a recursive cloning of the parent when this object is cloned.
  147. * - 0: a shares-a link, which ensures that clones of this object share the same parent.
  148. */
  149. private static final byte _ISAPARENT_FLAG_ = 1<<0;
  150. /**
  151. * This flag determines whether or not the field map of this object is shared by other objects:
  152. * - 1: the map is shared, so modifications must be performed on a copy
  153. * - 0: the map is not shared, modifications may be directly performed on it
  154. *
  155. * This flag is important for maintaining the semantics that clones are self-sufficient objects:
  156. * they share field names and methods only at the implementation-level.
  157. */
  158. private static final byte _SHARE_MAP_FLAG_ = 1<<1;
  159. /**
  160. * Similar to _SHARE_MAP_FLAG__ but for determining the shared status of the method dictionary.
  161. */
  162. private static final byte _SHARE_DCT_FLAG_ = 1<<2;
  163. /**
  164. * This flag determines whether or not the object is an isolate and hence pass-by-copy:
  165. * - 1: the object is an isolate, pass-by-copy and no lexical parent except for the root
  166. * - 0: the object is pass-by-reference and can have any lexical parent
  167. */
  168. private static final byte _IS_ISOLATE_FLAG_ = 1<<3;
  169. /**
  170. * An empty stripe array shared by those objects that do not have any stripes.
  171. */
  172. public static final ATStripe[] _NO_STRIPES_ = new ATStripe[0];
  173. /**
  174. * The flags of an AmbientTalk object encode the following boolean information:
  175. * Format: 0b0000idap where
  176. * p = parent flag: if set, dynamic parent is 'is-a' parent, otherwise 'shares-a' parent
  177. * a = shares map flag: if set, the map of this object is shared between clones
  178. * d = shares dictionary flag: if set, the method dictionary of this object is shared between clones
  179. * i = is isolate flag: if set, the object is passed by copy in inter-actor communication
  180. */
  181. private byte flags_;
  182. // inherited from NATCallframe:
  183. // private FieldMap variableMap_;
  184. // private Vector stateVector_;
  185. // private LinkedList customFields_;
  186. /**
  187. * The method dictionary of this object. It maps method selectors to ATMethod objects.
  188. */
  189. private MethodDictionary methodDictionary_;
  190. /**
  191. * The stripes with which this object has been striped.
  192. */
  193. protected ATStripe[] stripes_;
  194. /* ------------------
  195. * -- Constructors --
  196. * ------------------ */
  197. /**
  198. * Creates an object striped with the at.stripes.Isolate stripe.
  199. * Such an object is called an isolate because:
  200. * - it has no access to an enclosing lexical scope (except for the root lexical scope)
  201. * - it can therefore be passed by copy
  202. */
  203. public static NATObject createIsolate() {
  204. return new NATObject(new ATStripe[] { NativeStripes._ISOLATE_ });
  205. }
  206. /**
  207. * Constructs a new AmbientTalk object whose lexical parent is the
  208. * global scope and whose dynamic parent is the dynamic root.
  209. */
  210. public NATObject() {
  211. this(Evaluator.getGlobalLexicalScope());
  212. }
  213. public NATObject(ATStripe[] stripes) {
  214. this(Evaluator.getGlobalLexicalScope(), stripes);
  215. }
  216. /**
  217. * Constructs a new ambienttalk object parametrised by a lexical scope. The
  218. * object is thus not equipped with a pointer to a dynamic parent.
  219. * @param lexicalParent - the lexical scope in which the object's definition was nested
  220. */
  221. public NATObject(ATObject lexicalParent) {
  222. this(NATNil._INSTANCE_, lexicalParent, _SHARES_A_);
  223. }
  224. /**
  225. * Constructs a new ambienttalk object parametrised by a lexical scope.
  226. * The object's dynamic parent is nil and is striped with the given table of stripes
  227. */
  228. public NATObject(ATObject lexicalParent, ATStripe[] stripes) {
  229. this(NATNil._INSTANCE_, lexicalParent, _SHARES_A_, stripes);
  230. }
  231. /**
  232. * Constructs a new ambienttalk object with the given dynamic parent.
  233. * The lexical parent is assumed to be the global scope.
  234. * @param dynamicParent - the dynamic parent of the new object
  235. * @param parentType - the type of parent link
  236. */
  237. public NATObject(ATObject dynamicParent, boolean parentType) {
  238. this(dynamicParent, Evaluator.getGlobalLexicalScope(), parentType);
  239. }
  240. /**
  241. * Constructs a new ambienttalk object based on a set of parent pointers.
  242. * The object has no stripes.
  243. * @param dynamicParent - the parent object of the newly created object
  244. * @param lexicalParent - the lexical scope in which the object's definition was nested
  245. * @param parentType - how this object extends its dynamic parent (is-a or shares-a)
  246. */
  247. public NATObject(ATObject dynamicParent, ATObject lexicalParent, boolean parentType) {
  248. this(dynamicParent, lexicalParent, parentType, _NO_STRIPES_);
  249. }
  250. /**
  251. * Constructs a new ambienttalk object based on a set of parent pointers.
  252. * The object is striped with the given stripes.
  253. * @param dynamicParent - the parent object of the newly created object
  254. * @param lexicalParent - the lexical scope in which the object's definition was nested
  255. * @param parentType - how this object extends its dynamic parent (is-a or shares-a)
  256. * @param stripes - the stripes attached to this object
  257. */
  258. public NATObject(ATObject dynamicParent, ATObject lexicalParent, boolean parentType, ATStripe[] stripes) {
  259. super(lexicalParent);
  260. // by default, an object has a shares-a parent, does not share its map
  261. // or dictionary and is no isolate, so all flags are set to 0
  262. flags_ = 0;
  263. stripes_ = stripes;
  264. methodDictionary_ = new MethodDictionary();
  265. // bind the dynamic parent to the field named 'super'
  266. // we don't pass via meta_defineField as this would trigger mirages too early
  267. variableMap_.put(_SUPER_NAME_);
  268. stateVector_.add(dynamicParent);
  269. // add ==, new and init to the method dictionary directly
  270. // we don't pass via meta_addMethod as this would trigger mirages too early
  271. methodDictionary_.put(_EQL_NAME_, _PRIM_EQL_);
  272. methodDictionary_.put(_NEW_NAME_, _PRIM_NEW_);
  273. methodDictionary_.put(_INI_NAME_, _PRIM_INI_);
  274. if (parentType) { // parentType == _IS_A_)
  275. // requested an 'is-a' parent
  276. setFlag(_ISAPARENT_FLAG_); // set is-a parent flag to 1
  277. }
  278. try {
  279. // if this object is striped as at.stripes.Isolate, flag it as an isolate
  280. // we cannot perform 'this.meta_isStripedWith(ISOLATE)' because this would trigger mirages too early
  281. if (isLocallyStripedWith(NativeStripes._ISOLATE_)
  282. || dynamicParent.meta_isStripedWith(NativeStripes._ISOLATE_).asNativeBoolean().javaValue) {
  283. setFlag(_IS_ISOLATE_FLAG_);
  284. // isolates can only have the global lexical root as their lexical scope
  285. lexicalParent_ = Evaluator.getGlobalLexicalScope();
  286. }
  287. } catch (InterpreterException e) {
  288. // some custom stripe failed to match agains the Isolate stripe,
  289. // the object is not considered an Isolate
  290. Logging.Actor_LOG.error("Error testing for Isolate stripe, ignored:", e);
  291. }
  292. }
  293. /**
  294. * Constructs a new ambienttalk object as a clone of an existing object.
  295. *
  296. * The caller of this method *must* ensure that the shares flags are set.
  297. *
  298. * This constructor is responsible for manually re-initialising any custom field
  299. * objects, because the init method of such custom fields is parameterized by the
  300. * clone, which only comes into existence when this constructor runs.
  301. */
  302. protected NATObject(FieldMap map,
  303. Vector state,
  304. LinkedList originalCustomFields,
  305. MethodDictionary methodDict,
  306. ATObject dynamicParent,
  307. ATObject lexicalParent,
  308. byte flags,
  309. ATStripe[] stripes) throws InterpreterException {
  310. super(map, state, lexicalParent, null);
  311. methodDictionary_ = methodDict;
  312. flags_ = flags; //a cloned object inherits all flags from original
  313. // clone inherits all stripes (this implies that clones of isolates are also isolates)
  314. stripes_ = stripes;
  315. // ==, new and init should already be present in the method dictionary
  316. // set the 'super' field to point to the new dynamic parent
  317. setLocalField(_SUPER_NAME_, dynamicParent);
  318. // re-initialize all custom fields
  319. if (originalCustomFields != null) {
  320. customFields_ = new LinkedList();
  321. Iterator it = originalCustomFields.iterator();
  322. while (it.hasNext()) {
  323. ATField field = (ATField) it.next();
  324. customFields_.add(field.base_new(new ATObject[] { this }).asField());
  325. }
  326. }
  327. }
  328. /**
  329. * Initialize a new AmbientTalk object with the given closure.
  330. *
  331. * The closure encapsulates:
  332. * - the code with which to initialize the object
  333. * - the lexical parent of the object (but that parent should already be set)
  334. * - the lexically inherited fields for the object (the parameters of the closure)
  335. */
  336. public void initializeWithCode(ATClosure code) throws InterpreterException {
  337. NATTable copiedBindings = Evaluator.evalMandatoryPars(
  338. code.base_getMethod().base_getParameters(),
  339. code.base_getContext());
  340. code.base_applyInScope(copiedBindings, this);
  341. }
  342. /**
  343. * Invoke NATObject's primitive implementation, such that Java invocations of this
  344. * method have the same behaviour as AmbientTalk invocations.
  345. */
  346. public ATObject base_init(ATObject[] initargs) throws InterpreterException {
  347. return this.prim_init(this, initargs);
  348. }
  349. /**
  350. * The primitive implementation of init in objects is to invoke the init
  351. * method of their parent.
  352. * @param self the object that originally received the 'init' message.
  353. *
  354. * def init(@args) {
  355. * super^init(@args)
  356. * }
  357. */
  358. private ATObject prim_init(ATObject self, ATObject[] initargs) throws InterpreterException {
  359. ATObject parent = base_getSuper();
  360. return parent.meta_invoke(self, Evaluator._INIT_, NATTable.atValue(initargs));
  361. }
  362. /* ------------------------------
  363. * -- Message Sending Protocol --
  364. * ------------------------------ */
  365. /**
  366. * Invocations on an object ( <tt>o.m( args )</tt> ) are handled by looking up the requested
  367. * selector in the dynamic parent chain of the receiver. This dynamic lookup process
  368. * yields exactly the same result as a selection (e.g. <tt>o.m</tt>). The result
  369. * ought to be a closure (a method and its corresponding evaluation context), which
  370. * is applied to the provided arguments.
  371. *
  372. * The code for meta_invoke is actually equivalent to
  373. * <code>return this.meta_select(receiver, selector).asClosure().meta_apply(arguments);</code>
  374. * but has a specialized implementation for performance reasons (no unnecessary closure is created)
  375. */
  376. public ATObject meta_invoke(ATObject receiver, ATSymbol selector, ATTable arguments) throws InterpreterException {
  377. if (this.hasLocalField(selector)) {
  378. return this.getLocalField(selector).asClosure().base_apply(arguments);
  379. } else if (this.hasLocalMethod(selector)) {
  380. // immediately execute the method in the context ctx where
  381. // ctx.scope = the implementing scope, being this object, under which an additional callframe will be inserted
  382. // ctx.self = the late bound receiver, being the passed receiver
  383. return this.getLocalMethod(selector).base_apply(arguments, new NATContext(this, receiver));
  384. } else {
  385. return base_getSuper().meta_invoke(receiver, selector, arguments);
  386. }
  387. }
  388. /**
  389. * An ambienttalk object can respond to a message if a corresponding field or method exists
  390. * either in the receiver object locally, or in one of its dynamic parents.
  391. */
  392. public ATBoolean meta_respondsTo(ATSymbol selector) throws InterpreterException {
  393. if (this.hasLocalField(selector) || this.hasLocalMethod(selector))
  394. return NATBoolean._TRUE_;
  395. else
  396. return base_getSuper().meta_respondsTo(selector);
  397. }
  398. /* ------------------------------------------
  399. * -- Slot accessing and mutating protocol --
  400. * ------------------------------------------ */
  401. /**
  402. * meta_select is used to evaluate code of the form <tt>o.m</tt>.
  403. *
  404. * To select a slot from an object:
  405. * - first, the list of fields of the current receiver ('this') is searched.
  406. * If a matching field exists, its value is returned.
  407. * - second, the list of methods of the current receiver is searched.
  408. * If a matching method exists, it is returned, but wrapped in a closure.
  409. * This wrapping is vital to ensure that the method is paired with the correct 'self'.
  410. * This 'self' does not necessarily equal 'this'.
  411. * - third, the search for the slot is carried out recursively in the dynamic parent.
  412. * As such, slot selection traverses the dynamic parent chain up to a dynamic root.
  413. * The dynamic root deals with an unbound slot by sending the 'doesNotUnderstand' message
  414. * to the original receiver.
  415. *
  416. * @param receiver the original receiver of the selection
  417. * @param selector the selector to look up
  418. * @return the value of the found field, or a closure wrapping a found method
  419. */
  420. public ATObject meta_select(ATObject receiver, ATSymbol selector) throws InterpreterException {
  421. if (this.hasLocalField(selector)) {
  422. return this.getLocalField(selector);
  423. } else if (this.hasLocalMethod(selector)) {
  424. // return a new closure (mth, ctx) where
  425. // mth = the method found in this object
  426. // ctx.scope = the implementing scope, being this object
  427. // ctx.self = the late bound receiver, being the passed receiver
  428. // ctx.super = the parent of the implementor
  429. return new NATClosure(this.getLocalMethod(selector), this, receiver);
  430. } else {
  431. return base_getSuper().meta_select(receiver, selector);
  432. }
  433. }
  434. /**
  435. * This method corresponds to code of the form ( x ) within the scope of this
  436. * object. It searches for the requested selector among the methods and fields
  437. * of the object and its dynamic parents.
  438. *
  439. * Overridden from NATCallframe to take methods into account as well.
  440. *
  441. * This method is used to evaluate code of the form <tt>selector</tt> within the scope
  442. * of this object. An object resolves such a lookup request as follows:
  443. * - If a field corresponding to the selector exists locally, the field's value is returned.
  444. * - If a method corresponding to the selector exists locally, the method is wrapped
  445. * using the current object itself as implementor AND as 'self'.
  446. * The reason for setting the closure's 'self' to the implementor is because a lookup can only
  447. * be initiated by the object itself or a lexically nested one. Lexical nesting, however, has
  448. * nothing to do with dynamic delegation, and it would be wrong to bind 'self' to a nested object
  449. * which need not be a dynamic child of the implementor.
  450. *
  451. * - Otherwise, the search continues recursively in the object's lexical parent.
  452. */
  453. public ATObject meta_lookup(ATSymbol selector) throws InterpreterException {
  454. if (this.hasLocalField(selector)) {
  455. return this.getLocalField(selector);
  456. } else if (this.hasLocalMethod(selector)) {
  457. // return a new closure (mth, ctx) where
  458. // mth = the method found in this object
  459. // ctx.scope = the implementing scope, being this object
  460. // ctx.self = the receiver, being in this case again the implementor
  461. return new NATClosure(this.getLocalMethod(selector),this, this);
  462. } else {
  463. return lexicalParent_.meta_lookup(selector);
  464. }
  465. }
  466. /**
  467. * When a new field is defined in an object, it is important to check whether or not
  468. * the field map is shared between clones or not. If it is shared, the map must be cloned first.
  469. * @throws InterpreterException
  470. */
  471. public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
  472. if (this.isFlagSet(_SHARE_MAP_FLAG_)) {
  473. // copy the variable map
  474. variableMap_ = variableMap_.copy();
  475. // set the 'shares map' flag to false
  476. unsetFlag(_SHARE_MAP_FLAG_);
  477. }
  478. return super.meta_defineField(name, value);
  479. }
  480. /**
  481. * meta_assignField is used to evaluate code of the form <tt>o.m := v</tt>.
  482. *
  483. * To assign a field in an object:
  484. * - first, the list of fields of the current receiver ('this') is searched.
  485. * If a matching field exists, its value is set.
  486. * - If the field is not found, the search for the slot is carried out recursively in the dynamic parent.
  487. * As such, field assignment traverses the dynamic parent chain up to a dynamic root.
  488. * The dynamic root deals with an unbound field by throwing an error.
  489. * @param value the value to assign to the field
  490. * @param selector the field to assign
  491. *
  492. * @return NIL
  493. */
  494. public ATNil meta_assignField(ATObject receiver, ATSymbol selector, ATObject value) throws InterpreterException {
  495. if (this.setLocalField(selector, value)) {
  496. return NATNil._INSTANCE_;
  497. } else {
  498. return base_getSuper().meta_assignField(receiver, selector, value);
  499. }
  500. }
  501. /* ------------------------------------
  502. * -- Extension and cloning protocol --
  503. * ------------------------------------ */
  504. /**
  505. * When cloning an object, it is first determined whether the parent
  506. * has to be shared by the clone, or whether the parent must also be cloned.
  507. * This depends on whether the dynamic parent is an 'is-a' parent or a 'shares-a'
  508. * parent. This is determined by the _ISAPARENT_FLAG_ object flag.
  509. *
  510. * A cloned object shares with its original both the variable map
  511. * (to avoid having to copy space for field names) and the method dictionary
  512. * (method bindings are constant and can hence be shared).
  513. *
  514. * Should either the original or the clone later modify the map or the dictionary
  515. * (at the meta-level), the map or dictionary will be copied first. Hence,
  516. * sharing between clones is an implementation-level optimization: clones
  517. * are completely self-sufficient and do not influence one another by meta-level operations.
  518. */
  519. public ATObject meta_clone() throws InterpreterException {
  520. ATObject dynamicParent;
  521. if(this.isFlagSet(_ISAPARENT_FLAG_)) {
  522. // IS-A Relation : clone the dynamic parent.
  523. dynamicParent = base_getSuper().meta_clone();
  524. } else {
  525. // SHARES_A Relation : share the dynamic parent.
  526. dynamicParent = base_getSuper();
  527. }
  528. // ! set the shares flags of this object *and* of its clone
  529. // both this object and the clone now share the map and method dictionary
  530. setFlag(_SHARE_DCT_FLAG_);
  531. setFlag(_SHARE_MAP_FLAG_);
  532. NATObject clone = this.createClone(variableMap_,
  533. (Vector) stateVector_.clone(), // shallow copy
  534. customFields_, // must be re-initialized by clone!
  535. methodDictionary_,
  536. dynamicParent,
  537. lexicalParent_,
  538. flags_, stripes_);
  539. return clone;
  540. }
  541. /**
  542. * When new is invoked on an object's mirror, the object is first cloned
  543. * by the mirror, after which the method named 'init' is invoked on it.
  544. *
  545. * meta_newInstance(t) = base_init(t) o meta_clone
  546. *
  547. * Care should be taken that a shares-a child implements its own init method
  548. * which does NOT perform a super-send. If this is not the case, then it is
  549. * possible that a shared parent is accidentally re-initialized because a
  550. * sharing child is cloned via new.
  551. */
  552. public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
  553. ATObject clone = this.meta_clone();
  554. clone.meta_invoke(clone, Evaluator._INIT_, initargs);
  555. return clone;
  556. }
  557. public ATBoolean meta_isExtensionOfParent() throws InterpreterException {
  558. return NATBoolean.atValue(isFlagSet(_ISAPARENT_FLAG_));
  559. }
  560. /* ---------------------------------
  561. * -- Structural Access Protocol --
  562. * --------------------------------- */
  563. /**
  564. * When a method is added to an object, it is first checked whether the method does not
  565. * already exist. Also, care has to be taken that the method dictionary of an object
  566. * does not affect clones. Therefore, if the method dictionary is shared, a copy
  567. * of the dictionary is taken before adding the method.
  568. *
  569. * One exception to method addition are primitive methods: if the method added
  570. * would conflict with a primitive method, the primitive is replaced by the new
  571. * method instead.
  572. */
  573. public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
  574. ATSymbol name = method.base_getName();
  575. if (methodDictionary_.containsKey(name) && !isPrimitive(name)) {
  576. throw new XDuplicateSlot(XDuplicateSlot._METHOD_, name);
  577. } else {
  578. // first check whether the method dictionary is shared
  579. if (this.isFlagSet(_SHARE_DCT_FLAG_)) {
  580. methodDictionary_ = (MethodDictionary) methodDictionary_.clone();
  581. this.unsetFlag(_SHARE_DCT_FLAG_);
  582. }
  583. methodDictionary_.put(name, method);
  584. }
  585. return NATNil._INSTANCE_;
  586. }
  587. public ATMethod meta_grabMethod(ATSymbol selector) throws InterpreterException {
  588. ATMethod result = (ATMethod)methodDictionary_.get(selector);
  589. if(result == null) {
  590. throw new XSelectorNotFound(selector, this);
  591. } else {
  592. return result;
  593. }
  594. }
  595. public ATTable meta_listMethods() throws InterpreterException {
  596. Collection methods = methodDictionary_.values();
  597. return NATTable.atValue((ATObject[]) methods.toArray(new ATObject[methods.size()]));
  598. }
  599. public NATText meta_print() throws InterpreterException {
  600. if (stripes_.length == 0) {
  601. return NATText.atValue("<object:"+this.hashCode()+">");
  602. } else {
  603. return NATText.atValue("<object:"+this.hashCode()+
  604. Evaluator.printElements(stripes_, "[", ",", "]").javaValue+">");
  605. }
  606. }
  607. public boolean isCallFrame() {
  608. return false;
  609. }
  610. /* ---------------------
  611. * -- Mirror Fields --
  612. * --------------------- */
  613. // protected methods, may be adapted by extensions
  614. protected NATObject createClone(FieldMap map,
  615. Vector state,
  616. LinkedList originalCustomFields,
  617. MethodDictionary methodDict,
  618. ATObject dynamicParent,
  619. ATObject lexicalParent,
  620. byte flags,
  621. ATStripe[] stripes) throws InterpreterException {
  622. return new NATObject(map,
  623. state,
  624. originalCustomFields,
  625. methodDict,
  626. dynamicParent,
  627. lexicalParent,
  628. flags,
  629. stripes);
  630. }
  631. /* ----------------------------------
  632. * -- Object Relational Comparison --
  633. * ---------------------------------- */
  634. public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
  635. if(original instanceof NATObject) {
  636. MethodDictionary originalMethods = ((NATObject)original).methodDictionary_;
  637. FieldMap originalVariables = ((NATObject)original).variableMap_;
  638. return NATBoolean.atValue(
  639. methodDictionary_.isDerivedFrom(originalMethods) &
  640. variableMap_.isDerivedFrom(originalVariables));
  641. } else {
  642. return NATBoolean._FALSE_;
  643. }
  644. }
  645. public ATBoolean meta_isRelatedTo(final ATObject object) throws InterpreterException {
  646. return this.meta_isCloneOf(object).base_or_(
  647. new NativeClosure(this) {
  648. public ATObject base_apply(ATTable args) throws InterpreterException {
  649. return scope_.base_getSuper().meta_isRelatedTo(object);
  650. }
  651. }).asBoolean();
  652. }
  653. /* ---------------------------------
  654. * -- Stripe Testing and Querying --
  655. * --------------------------------- */
  656. /**
  657. * Check whether one of the stripes of this object is a substripe of the given stripe.
  658. * If not, then delegate the query to the dynamic parent.
  659. */
  660. public ATBoolean meta_isStripedWith(ATStripe stripe) throws InterpreterException {
  661. if (isLocallyStripedWith(stripe)) {
  662. return NATBoolean._TRUE_;
  663. } else {
  664. // no stripes match, ask the parent
  665. return base_getSuper().meta_isStripedWith(stripe);
  666. }
  667. }
  668. /**
  669. * Return the stripes that were directly attached to this object.
  670. */
  671. public ATTable meta_getStripes() throws InterpreterException {
  672. // make a copy of the internal stripes array to ensure that the stripes
  673. // of the object are immutable. Tables allow assignment!
  674. if (stripes_.length == 0) {
  675. return NATTable.EMPTY;
  676. } else {
  677. ATStripe[] stripes = new ATStripe[stripes_.length];
  678. System.arraycopy(stripes_, 0, stripes, 0, stripes_.length);
  679. return NATTable.atValue(stripes);
  680. }
  681. }
  682. // NATObject has to duplicate the NATByCopy implementation
  683. // because NATObject inherits from NATByRef, and because Java has no
  684. // multiple inheritance to override that implementation with that of
  685. // NATByCopy if this object signifies an isolate.
  686. /**
  687. * An isolate object does not return a proxy representation of itself
  688. * during serialization, hence it is serialized itself. If the object
  689. * is not an isolate, invoke the default behaviour for by-reference objects
  690. */
  691. public ATObject meta_pass() throws InterpreterException {
  692. if (isFlagSet(_IS_ISOLATE_FLAG_)) {
  693. return this;
  694. } else {
  695. return super.meta_pass();
  696. }
  697. }
  698. /**
  699. * An isolate object represents itself upon deserialization.
  700. * If this object is not an isolate, the default behaviour for by-reference
  701. * objects is invoked.
  702. */
  703. public ATObject meta_resolve() throws InterpreterException {
  704. if (isFlagSet(_IS_ISOLATE_FLAG_)) {
  705. // re-bind to the new local global lexical root
  706. lexicalParent_ = Evaluator.getGlobalLexicalScope();
  707. return this;
  708. } else {
  709. return super.meta_resolve();
  710. }
  711. }
  712. /* ---------------------------------------
  713. * -- Conversion and Testing Protocol --
  714. * --------------------------------------- */
  715. public NATObject asAmbientTalkObject() { return this; }
  716. /**
  717. * ALL asXXX methods return a coercer object which returns a proxy of the correct interface that will 'down'
  718. * subsequent Java base-level invocations to the AmbientTalk level.
  719. *
  720. * Coercion only happens if the object is striped with the correct stripe.
  721. */
  722. private Object coerce(ATStripe requiredStripe, Class providedInterface) throws InterpreterException {
  723. if (this.meta_isStripedWith(requiredStripe).asNativeBoolean().javaValue) {
  724. return Coercer.coerce(this, providedInterface);
  725. } else {
  726. // if the object does not possess the right stripe, raise a type error
  727. throw new XTypeMismatch(providedInterface, this);
  728. }
  729. }
  730. public ATBoolean asBoolean() throws InterpreterException { return (ATBoolean) coerce(NativeStripes._BOOLEAN_, ATBoolean.class); }
  731. public ATClosure asClosure() throws InterpreterException { return (ATClosure) coerce(NativeStripes._CLOSURE_, ATClosure.class); }
  732. public ATExpression asExpression() throws InterpreterException { return (ATExpression) coerce(NativeStripes._EXPRESSION_, ATExpression.class); }
  733. public ATField asField() throws InterpreterException { return (ATField) coerce(NativeStripes._FIELD_, ATField.class); }
  734. public ATMessage asMessage() throws InterpreterException { return (ATMessage) coerce(NativeStripes._MESSAGE_, ATMessage.class); }
  735. public ATMethod asMethod() throws InterpreterException { return (ATMethod) coerce(NativeStripes._METHOD_, ATMethod.class); }
  736. public ATHandler asHandler() throws InterpreterException { return (ATHandler) coerce(NativeStripes._HANDLER_, ATHandler.class); }
  737. public ATNumber asNumber() throws InterpreterException { return (ATNumber) coerce(NativeStripes._NUMBER_, ATNumber.class); }
  738. public ATTable asTable() throws InterpreterException { return (ATTable) coerce(NativeStripes._TABLE_, ATTable.class); }
  739. public ATAsyncMessage asAsyncMessage() throws InterpreterException { return (ATAsyncMessage) coerce(NativeStripes._ASYNCMSG_, ATAsyncMessage.class);}
  740. public ATActorMirror asActorMirror() throws InterpreterException { return (ATActorMirror) coerce(NativeStripes._ACTORMIRROR_, ATActorMirror.class); }
  741. public ATStripe asStripe() throws InterpreterException { return (ATStripe) coerce(NativeStripes._STRIPE_, ATStripe.class); }
  742. public ATBegin asBegin() throws InterpreterException { return (ATBegin) coerce(NativeStripes._BEGIN_, ATBegin.class); }
  743. public ATStatement asStatement() throws InterpreterException { return (ATStatement) coerce(NativeStripes._STATEMENT_, ATStatement.class); }
  744. public ATUnquoteSplice asUnquoteSplice() throws InterpreterException { return (ATUnquoteSplice) coerce(NativeStripes._UQSPLICE_, ATUnquoteSplice.class); }
  745. public ATSymbol asSymbol() throws InterpreterException { return (ATSymbol) coerce(NativeStripes._SYMBOL_, ATSymbol.class); }
  746. public ATSplice asSplice() throws InterpreterException { return (ATSplice) coerce(NativeStripes._SPLICE_, ATSplice.class); }
  747. public ATDefinition asDefinition() throws InterpreterException { return (ATDefinition) coerce(NativeStripes._DEFINITION_, ATDefinition.class); }
  748. public ATMessageCreation asMessageCreation() throws InterpreterException { return (ATMessageCreation) coerce(NativeStripes._MSGCREATION_, ATMessageCreation.class); }
  749. // ALL isXXX methods return true (can be overridden by programmer-defined base-level methods)
  750. public boolean isAmbientTalkObject() { return true; }
  751. // objects can only be 'cast' to a native category if they are marked with
  752. // the appropriate native stripe
  753. public boolean isBoolean() throws InterpreterException { return meta_isStripedWith(NativeStripes._BOOLEAN_).asNativeBoolean().javaValue; }
  754. public boolean isClosure() throws InterpreterException { return meta_isStripedWith(NativeStripes._CLOSURE_).asNativeBoolean().javaValue; }
  755. public boolean isMethod() throws InterpreterException { return meta_isStripedWith(NativeStripes._METHOD_).asNativeBoolean().javaValue; }
  756. public boolean isSplice() throws InterpreterException { return meta_isStripedWith(NativeStripes._SPLICE_).asNativeBoolean().javaValue; }
  757. public boolean isSymbol() throws InterpreterException { return meta_isStripedWith(NativeStripes._SYMBOL_).asNativeBoolean().javaValue; }
  758. public boolean isTable() throws InterpreterException { return meta_isStripedWith(NativeStripes._TABLE_).asNativeBoolean().javaValue; }
  759. public boolean isUnquoteSplice() throws InterpreterException { return meta_isStripedWith(NativeStripes._UQSPLICE_).asNativeBoolean().javaValue; }
  760. public boolean isStripe() throws InterpreterException { return meta_isStripedWith(NativeStripes._STRIPE_).asNativeBoolean().javaValue; }
  761. // private methods
  762. private boolean isFlagSet(byte flag) {
  763. return (flags_ & flag) != 0;
  764. }
  765. private void setFlag(byte flag) {
  766. flags_ = (byte) (flags_ | flag);
  767. }
  768. private void unsetFlag(byte flag) {
  769. flags_ = (byte) (flags_ & (~flag));
  770. }
  771. private boolean hasLocalMethod(ATSymbol selector) {
  772. return methodDictionary_.containsKey(selector);
  773. }
  774. private ATMethod getLocalMethod(ATSymbol selector) throws InterpreterException {
  775. ATMethod result = ((ATObject) methodDictionary_.get(selector)).asMethod();
  776. if(result == null) {
  777. throw new XSelectorNotFound(selector, this);
  778. } else {
  779. return result;
  780. }
  781. }
  782. /**
  783. * Performs a stripe test for this object locally.
  784. * @return whether this object is striped with a particular stripe or not.
  785. */
  786. private boolean isLocallyStripedWith(ATStripe stripe) throws InterpreterException {
  787. for (int i = 0; i < stripes_.length; i++) {
  788. if (stripes_[i].base_isSubstripeOf(stripe).asNativeBoolean().javaValue) {
  789. // if one stripe matches, return true
  790. return true;
  791. }
  792. }
  793. return false;
  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 != NATNil._INSTANCE_ ; obj = obj.base_getSuper()) {
  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_getName();
  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. * Auxiliary method to access the methods of an object and all of its super-objects up to (but excluding) nil.
  817. * Overridden methods of parent objects are not included.
  818. */
  819. public static ATMethod[] listTransitiveMethods(ATObject obj) throws InterpreterException {
  820. Vector methods = new Vector();
  821. HashSet encounteredNames = new HashSet(); // to filter duplicates
  822. for (; obj != NATNil._INSTANCE_ ; obj = obj.base_getSuper()) {
  823. // fast-path for native objects
  824. if (obj instanceof NATObject) {
  825. Collection localMethods = ((NATObject) obj).methodDictionary_.values();
  826. for (Iterator iter = localMethods.iterator(); iter.hasNext();) {
  827. ATMethod localMethod = (ATMethod) iter.next();
  828. ATSymbol methodName = localMethod.base_getName();
  829. if (!encounteredNames.contains(methodName)) {
  830. methods.add(localMethod);
  831. encounteredNames.add(methodName);
  832. }
  833. }
  834. } else {
  835. ATObject[] localMethods = obj.meta_listMethods().asNativeTable().elements_;
  836. for (int i = 0; i < localMethods.length; i++) {
  837. ATMethod localMethod = localMethods[i].asMethod();
  838. ATSymbol methodName = localMethod.base_getName();
  839. if (!encounteredNames.contains(methodName)) {
  840. methods.add(localMethod);
  841. encounteredNames.add(methodName);
  842. }
  843. }
  844. }
  845. }
  846. return (ATMethod[]) methods.toArray(new ATMethod[methods.size()]);
  847. }
  848. }