/interpreter/tags/at2dist130208/test/edu/vub/at/objects/natives/ExceptionHandlingTest.java

http://ambienttalk.googlecode.com/ · Java · 305 lines · 185 code · 25 blank · 95 comment · 0 complexity · ba67ac41fa887e687e48a363ce78e646 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * ExceptionHandlingTest.java created on Oct 10, 2006 at 10:34:10 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.AmbientTalkTest;
  30. import edu.vub.at.AmbientTalkTestCase;
  31. import edu.vub.at.eval.Evaluator;
  32. import edu.vub.at.exceptions.InterpreterException;
  33. import edu.vub.at.exceptions.XSelectorNotFound;
  34. import edu.vub.at.objects.ATObject;
  35. import edu.vub.at.objects.coercion.NativeTypeTags;
  36. import edu.vub.at.objects.natives.grammar.AGSymbol;
  37. import edu.vub.at.objects.symbiosis.JavaClass;
  38. /**
  39. * This test documents and tests the behaviour of the exception handling primitives
  40. * provided in Ambienttalk. In AmbientTalk any object can be used and thrown as an
  41. * exception. Moreover, by relying on in types, all subtyping issues regarding to
  42. * handler selection are handled by the isTaggedAs meta operation.
  43. *
  44. * @author smostinc
  45. */
  46. public class ExceptionHandlingTest extends AmbientTalkTestCase {
  47. /**
  48. * Any AmbientTalk Language value can be used as an exception, proving there is nothing
  49. * special about exceptions. This test demonstrates this for a number of language values,
  50. * such as numbers, tables, closures and type tags themselves.
  51. *
  52. * This test also shows that objects can be caught using the type they are tagged with.
  53. * Note that most often, exceptions will be isolates, as these are objects (which can be
  54. * made arbitrarily complex) which are passed by copy between actors. However, as the
  55. * type tags of a far reference are identical to the one of the original object, this
  56. * semantics is a default upon which can be varied.
  57. */
  58. public void notestAllObjectsCanBeUsedAsExceptions() {
  59. try {
  60. evaluateInput(
  61. "def testCount := 0; \n" +
  62. "raise: 42 catch: Number do: { | exc | testCount := testCount + 1 }; \n" +
  63. "raise: [ 4, 8, 15, 16, 23, 42 ] catch: Table do: { | exc | testCount := testCount + 1 }; \n" +
  64. "raise: { testCount := testCount + 1 } catch: Closure do: { | exc | exc.apply([]) }; \n" +
  65. "raise: Number catch: TypeTag do: { | exc | testCount := testCount + 1 }; \n" +
  66. "if: testCount != 4 then: { fail() }", ctx_);
  67. } catch (InterpreterException e) {
  68. e.printStackTrace();
  69. fail("exception: "+ e);
  70. }
  71. };
  72. /**
  73. * Handler selection in AmbientTalk is purely based on the types an object is tagged with.
  74. * As such the correct handler selection can be delegated to the type system which ensures
  75. * correct handler selection.
  76. */
  77. public void testTypeBasedHandlerSelection() {
  78. try {
  79. evaluateInput(
  80. "deftype MyExceptions; \n" +
  81. "deftype IncorrectArguments <: MyExceptions; \n" +
  82. "def testCount := 0; \n" +
  83. "try: { \n" +
  84. " raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ IncorrectArguments ]) catch: IncorrectArguments do: { | exc | testCount := testCount + 1 }; \n" +
  85. " raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ IncorrectArguments ]) catch: MyExceptions do: { | exc | testCount := testCount + 1 }; \n" +
  86. " raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ MyExceptions ]) catch: MyExceptions do: { | exc | testCount := testCount + 1 }; \n" +
  87. " raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ MyExceptions ]) catch: IncorrectArguments do: { | exc | testCount := testCount + 1 }; \n" +
  88. "} catch: MyExceptions using: { | exc | \n" +
  89. // the last test will result in an uncaught exception, but all previous ones should have been handled.
  90. " if: testCount != 3 then: { fail() } \n" +
  91. "}", ctx_);
  92. } catch (InterpreterException e) {
  93. e.printStackTrace();
  94. fail("exception: "+ e);
  95. }
  96. }
  97. /**
  98. * The exceptions thrown by the interpreter can be intercepted by a program as they are also
  99. * typed objects, typed to identify their function. This test illustrates how to use this
  100. * mechanism to build a python-like object model where non-existant field are silently added
  101. * to the object upon use.
  102. *
  103. * Note that an altogether cleaner mechanism can be conceived by using the doesNotUnderstand
  104. * meta hook to achieve similar behaviour.
  105. */
  106. public void testInterpreterExceptionHandling() {
  107. try {
  108. evaluateInput(
  109. "def defaultValue := 42;" +
  110. "def pythonObjectMirror := \n" +
  111. " mirror: { \n" +
  112. " def invokeField(receiver, symbol) {" +
  113. " self.invoke(receiver,symbol,[])" +
  114. " };" +
  115. " def invoke( receiver, symbol, args ) { \n" +
  116. " try: { \n" +
  117. " super^invoke( receiver, symbol, args ); \n" +
  118. " } catch: SelectorNotFound using: { | e | \n" +
  119. " super^defineField( symbol, defaultValue ); \n" +
  120. " defaultValue; \n" +
  121. " } \n" +
  122. " } \n" +
  123. " }; \n" +
  124. "def test := object: { nil } \n" +
  125. " mirroredBy: pythonObjectMirror; \n" +
  126. "if: (test.x != defaultValue) then: { fail(); };",
  127. ctx_);
  128. } catch (InterpreterException e) {
  129. e.printStackTrace();
  130. fail("exception: "+ e);
  131. }
  132. }
  133. /**
  134. * To avoid improper interference with the interpreter, user code should never throw
  135. * interpreter exceptions. However, in the light that various components of the language
  136. * may be reimplemented in the language itself, this functionality is supported by the
  137. * interpreter anyhow.
  138. *
  139. * Note that given the ability to catch interpreter exceptions, the programmer automatically
  140. * has the right to throw them as well, either by rethrowing them, or by storing it as a
  141. * prototype, of which new clones can be instatiated whenever he feels like it.
  142. *
  143. * This test consist of an object model where access to a field can be forbidden by throwing
  144. * an interpreter exception (tagged with SelectorNotFound)
  145. */
  146. public void testInterpreterExceptionThrowing() {
  147. try {
  148. AmbientTalkTest.evalSnippet(ExceptionHandlingTest.class, "snippet1", ctx_);
  149. // fail if no exception was thrown by the code.
  150. fail();
  151. } catch (XSelectorNotFound e) {
  152. // 1. Raising a Java Exception Successfull
  153. } catch (InterpreterException e) {
  154. e.printStackTrace();
  155. fail("exception: "+ e);
  156. }
  157. }
  158. /**
  159. * When rethrowing an exception from a handler, the expected semantics apply : no handlers
  160. * from the same try block are tried, even if they also match the thrown exception. Handlers
  161. * from a try block higher up the stack can however apply.
  162. *
  163. */
  164. public void testRethrownExceptions() {
  165. try {
  166. evaluateInput(
  167. "deftype MyExceptions; \n" +
  168. "deftype IncorrectArguments <: MyExceptions; \n" +
  169. "\n" +
  170. "def result := false;" +
  171. "def test := object: { \n" +
  172. " def [ message, stackTrace ] := [ nil, nil ]; \n" +
  173. " def test := 0; \n" +
  174. "} taggedAs: [ IncorrectArguments ]; \n" +
  175. "\n" +
  176. "try: { \n" +
  177. " try: { \n" +
  178. " raise: test; \n" +
  179. " } catch: MyExceptions using: { | exc | \n" +
  180. " result := true; \n" +
  181. " raise: exc; \n" +
  182. " } catch: IncorrectArguments using: { | exc |" +
  183. " fail();" +
  184. " }" +
  185. "} catch: SelectorNotFound using: { | exc | \n" +
  186. " fail(); \n" +
  187. "} catch: IncorrectArguments using: { | exc | \n" +
  188. " result := result.and: { true }; \n" +
  189. "}; \n" +
  190. "\n" +
  191. "if: (! result) then: { fail(); }",
  192. ctx_);
  193. } catch (InterpreterException e) {
  194. e.printStackTrace();
  195. fail("exception: "+ e);
  196. }
  197. }
  198. /**
  199. * Tests whether finally: blocks are called when <ol>
  200. * <li>the try block terminates normally
  201. * <li>a handler was invoked
  202. * <li>no matching handler was found
  203. * <li>the escape construct was used
  204. * </ol>
  205. */
  206. public void testFinallyConstruct() throws InterpreterException {
  207. evaluateInput(
  208. "{ | return | \n" +
  209. " try: { nil } \n" +
  210. " catch: SelectorNotFound using: { fail() } \n" +
  211. " finally: { return(true) }; \n" +
  212. " fail() \n" +
  213. "}.escape();", ctx_);
  214. evaluateInput(
  215. "def result1 := { | return | \n" +
  216. " try: { self.result } \n" +
  217. " catch: SelectorNotFound using: { false } \n" +
  218. " finally: { return(true) }; \n" +
  219. "}.escape(); \n" +
  220. "result1.ifFalse: { fail() };", ctx_);
  221. evaluateInput(
  222. "deftype WrongType;" +
  223. "def result2 := { | return | \n" +
  224. " try: { self.result } \n" +
  225. " catch: WrongType using: { fail() } \n" +
  226. " finally: { return(true) }; \n" +
  227. " false \n" +
  228. "}.escape(); \n" +
  229. "result2.ifFalse: { fail() };", ctx_);
  230. evaluateInput(
  231. "def result3 := { | return | \n" +
  232. " try: { self.result } \n" +
  233. " catch: SelectorNotFound using: { return(false) } \n" +
  234. " finally: { return(true) }; \n" +
  235. " false \n" +
  236. "}.escape(); \n" +
  237. "result3.ifFalse: { fail() };", ctx_);
  238. }
  239. public static void main(String[] args) {
  240. junit.swingui.TestRunner.run(ExceptionHandlingTest.class);
  241. }
  242. // For testing purposes we need access to a set of native type tags
  243. // These are introduced into the global lexical scope of the test
  244. private void setUpTestTypes(ATObject testScope) throws Exception {
  245. // Primitive type tags used to test all objects can be thrown
  246. testScope.meta_defineField(
  247. AGSymbol.jAlloc("Number"),
  248. NativeTypeTags._NUMBER_);
  249. testScope.meta_defineField(
  250. AGSymbol.jAlloc("Table"),
  251. NativeTypeTags._TABLE_);
  252. testScope.meta_defineField(
  253. AGSymbol.jAlloc("Closure"),
  254. NativeTypeTags._CLOSURE_);
  255. testScope.meta_defineField(
  256. AGSymbol.jAlloc("TypeTag"),
  257. NativeTypeTags._TYPETAG_);
  258. testScope.meta_defineField(
  259. AGSymbol.jAlloc("SelectorNotFound"),
  260. NativeTypeTags._SELECTORNOTFOUND_);
  261. }
  262. public void setUp() throws Exception {
  263. ATObject globalLexScope = Evaluator.getGlobalLexicalScope();
  264. ATObject testScope = new NATCallframe(globalLexScope);
  265. setUpTestTypes(testScope);
  266. // For throwing InterpreterExceptions, we provide a useful prototype to start from
  267. testScope.meta_defineField(
  268. AGSymbol.jAlloc("doesNotUnderstandX"),
  269. JavaClass.wrapperFor(XSelectorNotFound.class));
  270. ctx_ = new NATContext(testScope, globalLexScope);
  271. // Aux method to aid in shortening the test code
  272. evaluateInput(
  273. "def raise: exception catch: typetag do: closure { \n" +
  274. " try: {" +
  275. " raise: exception;" +
  276. " } catch: typetag using: closure; \n" +
  277. "}",
  278. ctx_);
  279. }
  280. }