help-smalltalk
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Help-smalltalk] [PATCH] Add BlockClosure>>#cull: and friends


From: Paolo Bonzini
Subject: [Help-smalltalk] [PATCH] Add BlockClosure>>#cull: and friends
Date: Mon, 12 May 2008 16:04:04 +0200
User-agent: Thunderbird 2.0.0.14 (Macintosh/20080421)

These are a bit cleverly named, but that's what the VW guys adopted. It's extended versions of #valueWithPossibleArgument: (which never entered GNU Smalltalk because of the ugly name, but is there in Squeak).

I chose to implement it in the VM for speed and because the needed code is possibly less than with a pure Smalltalk implementation.

Paolo
2008-05-12  Paolo Bonzini  <address@hidden>

        * kernel/BlkClosure.st: Add #cull:, #cull:cull:, #cull:cull:cull:.
        * kernel/ExcHandling.st: Use it for the exception handlers.
        * kernel/Object.st: Use it for #ifNotNil:.
        * tests/blocks.st: New tests.
        * tests/blocks.ok: Regenerate.

libgst:
2008-05-12  Paolo Bonzini  <address@hidden>

        * libgst/interp.c: Adjust send_block_value prototype.
        * libgst/interp-bc.inl: Support block argument culling.
        * libgst/interp-jit.inl: Support block argument culling.
        * libgst/prims.def: Add primitives for block argument culling.
        * libgst/vm.def: Adjust calls to send_block_value.


diff --git a/NEWS b/NEWS
index 3a968e9..634e68b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,11 @@ List of user-visible changes in GNU Smalltalk
 
 NEWS FROM 3.0.2 TO 3.0a
 
+o   BlockClosure methods #cull:, #cull:cull:, #cull:cull:cull:
+    evaluate blocks removing parameters that are not accepted by
+    the block.  Thanks to this new functionality, the parameter to
+    #on:do: and #ifNotNil: can be omitted.
+
 o   CObjects can be backed with garbage-collected (as opposed to
     heap-allocated) storage.  Using this is not always possible, for
     example for CObjects stored by external libraries or passed to
diff --git a/examples/Case.st b/examples/Case.st
index 3dd6054..1ec28d0 100644
--- a/examples/Case.st
+++ b/examples/Case.st
@@ -59,12 +59,12 @@ test: anObject
 !Case methodsFor: 'testing'!
 
 test: anObject
-    test _ anObject.
-    found _ false.
+    test := anObject.
+    found := false.
 !
 
 reset
-    found _ false
+    found := false
 !
 
 else: aBlock
@@ -94,10 +94,8 @@ when: aBlock do: aBlock2
 !Case methodsFor: 'private'!
 
 do: aBlock
-    found _ true.
-    ^result := (aBlock numArgs = 0
-       ifTrue: [ aBlock value ]
-       ifFalse: [ aBlock value: test ])
+    found := true.
+    ^result := (aBlock cull: test)
 ! !
 
 
diff --git a/kernel/BlkClosure.st b/kernel/BlkClosure.st
index 0b45506..63c9446 100644
--- a/kernel/BlkClosure.st
+++ b/kernel/BlkClosure.st
@@ -410,14 +410,11 @@ creation of Processes from blocks.'>
 
     forkWithoutPreemption [
        "Evaluate the receiver in a process that cannot be preempted.
-        If the receiver expect a parameter, pass the current process
-        (can be useful for queuing interrupts from within the
-        uninterruptible process)."
+        If the receiver expect a parameter, pass the current process."
 
        <category: 'multiple process'>
        | closure args process result |
-       closure := [self valueWithArguments: args].
-       args := self numArgs = 0 ifTrue: [#()] ifFalse: [{Processor 
activeProcess}].
+       closure := [self cull: Processor activeProcess].
        ^Process 
            on: closure
            at: Processor unpreemptedPriority
@@ -590,6 +587,33 @@ creation of Processes from blocks.'>
        SystemExceptions.WrongArgumentCount signal
     ]
 
+    cull: arg1 [
+       "Evaluate the receiver, passing arg1 as the only parameter if
+        the receiver has parameters."
+
+       <category: 'built ins'>
+       <primitive: VMpr_BlockClosure_cull>
+       SystemExceptions.WrongArgumentCount signal
+    ]
+
+    cull: arg1 cull: arg2 [
+       "Evaluate the receiver, passing arg1 and arg2 as parameters if
+        the receiver accepts them."
+
+       <category: 'built ins'>
+       <primitive: VMpr_BlockClosure_cull>
+       SystemExceptions.WrongArgumentCount signal
+    ]
+
+    cull: arg1 cull: arg2 cull: arg3 [
+       "Evaluate the receiver, passing arg1, arg2 and arg3 as parameters if
+        the receiver accepts them."
+
+       <category: 'built ins'>
+       <primitive: VMpr_BlockClosure_cull>
+       SystemExceptions.WrongArgumentCount signal
+    ]
+
     valueWithArguments: argumentsArray [
        "Evaluate the receiver passing argArray's elements as the parameters"
 
diff --git a/kernel/ExcHandling.st b/kernel/ExcHandling.st
index 31b4b23..4e8eb7d 100644
--- a/kernel/ExcHandling.st
+++ b/kernel/ExcHandling.st
@@ -684,7 +684,7 @@ with a lower priority.'>
                [:object | 
                self resetHandler.
                ^object].
-       result := handlerBlock value: self.
+       result := handlerBlock cull: self.
        resumeBoolean 
            ifTrue: 
                [self resetHandler.
diff --git a/kernel/Object.st b/kernel/Object.st
index 90e0bff..733bbd9 100644
--- a/kernel/Object.st
+++ b/kernel/Object.st
@@ -143,7 +143,7 @@ All classes in the system are subclasses of me.'>
         notNilBlock, passing the receiver."
 
        <category: 'testing functionality'>
-       ^notNilBlock value: self
+       ^notNilBlock cull: self
     ]
 
     ifNotNil: notNilBlock [
@@ -151,7 +151,7 @@ All classes in the system are subclasses of me.'>
         Else answer nil."
 
        <category: 'testing functionality'>
-       ^notNilBlock value: self
+       ^notNilBlock cull: self
     ]
 
     ifNotNil: notNilBlock ifNil: nilBlock [
@@ -159,7 +159,7 @@ All classes in the system are subclasses of me.'>
         notNilBlock, passing the receiver."
 
        <category: 'testing functionality'>
-       ^notNilBlock value: self
+       ^notNilBlock cull: self
     ]
 
     isCObject [
diff --git a/libgst/interp-bc.inl b/libgst/interp-bc.inl
index cafa13b..093940f 100644
--- a/libgst/interp-bc.inl
+++ b/libgst/interp-bc.inl
@@ -396,7 +396,7 @@ _gst_send_method (OOP methodOOP)
 
 
 static mst_Boolean
-send_block_value (int numArgs)
+send_block_value (int numArgs, int cull_up_to)
 {
   OOP closureOOP;
   block_header header;
@@ -406,10 +406,15 @@ send_block_value (int numArgs)
   closureOOP = STACK_AT (numArgs);
   closure = (gst_block_closure) OOP_TO_OBJ (closureOOP);
   header = ((gst_compiled_block) OOP_TO_OBJ (closure->block))->header;
+
+  /* Check numArgs.  Remove up to CULL_UP_TO extra arguments if needed.  */
   if UNCOMMON (numArgs != header.numArgs)
     {
-      /* check numArgs asap */
-      return (true);
+      if (numArgs < header.numArgs || numArgs > header.numArgs + cull_up_to)
+        return (true);
+
+      POP_N_OOPS (numArgs - header.numArgs);
+      numArgs = header.numArgs;
     }
 
   /* prepare the new state, loading data from the closure */
diff --git a/libgst/interp-jit.inl b/libgst/interp-jit.inl
index 297a8f6..8f7c291 100644
--- a/libgst/interp-jit.inl
+++ b/libgst/interp-jit.inl
@@ -286,7 +286,7 @@ _gst_send_method (OOP methodOOP)
 }
 
 static mst_Boolean
-send_block_value (int numArgs)
+send_block_value (int numArgs, int cull_up_to)
 {
   OOP closureOOP;
   OOP receiverClass;
@@ -296,10 +296,15 @@ send_block_value (int numArgs)
   closureOOP = STACK_AT (numArgs);
   closure = (gst_block_closure) OOP_TO_OBJ (closureOOP);
   header = ((gst_compiled_block) OOP_TO_OBJ (closure->block))->header;
+
+  /* Check numArgs.  Remove up to CULL_UP_TO extra arguments if needed.  */
   if UNCOMMON (numArgs != header.numArgs)
     {
-      /* check numArgs asap */
-      return (true);
+      if (numArgs < header.numArgs || numArgs > header.numArgs + cull_up_to)
+        return (true);
+
+      POP_N_OOPS (numArgs - header.numArgs);
+      numArgs = header.numArgs;
     }
 
   receiverClass = IS_INT (closure->receiver)
diff --git a/libgst/interp.c b/libgst/interp.c
index 18d2573..18c661f 100644
--- a/libgst/interp.c
+++ b/libgst/interp.c
@@ -424,9 +424,10 @@ static inline OOP create_args_array (int numArgs);
    the arguments in the block context, which have been copied out of
    the caller's context. 
 
-   On failure return true, on success (i.e. if NUMARGS matches what
-   the BlockClosure says) return false.  */
-static mst_Boolean send_block_value (int numArgs);
+   The block should accept between NUMARGS - CULL_UP_TO and
+   NUMARGS arguments.  If this is not true (failure) return true;
+   on success return false.  */
+static mst_Boolean send_block_value (int numArgs, int cull_up_to);
 
 /* This is a kind of simplified _gst_send_message_internal that,
    instead of setting up a context for a particular receiver, stores
diff --git a/libgst/prims.def b/libgst/prims.def
index 5f242cc..6237fd8 100644
--- a/libgst/prims.def
+++ b/libgst/prims.def
@@ -2581,7 +2581,19 @@ primitive VMpr_Continuation_resume [fail,reload_ip]
 primitive VMpr_BlockClosure_value [fail,reload_ip,cache_new_ip]
 {
   _gst_primitives_executed++;
-  if UNCOMMON (send_block_value (numArgs))
+  if UNCOMMON (send_block_value (numArgs, 0))
+    PRIM_FAILED;
+  else
+    PRIM_SUCCEEDED_RELOAD_IP;
+}
+
+/* BlockClosure cull:
+   BlockClosure cull:cull:
+   BlockClosure cull:cull:cull: */
+primitive VMpr_BlockClosure_cull [fail,reload_ip]
+{
+  _gst_primitives_executed++;
+  if UNCOMMON (send_block_value (numArgs, numArgs))
     PRIM_FAILED;
   else
     PRIM_SUCCEEDED_RELOAD_IP;
@@ -2598,7 +2610,7 @@ primitive VMpr_BlockClosure_valueAndResumeOnUnwind 
[fail,reload_ip]
 
   context = (gst_method_context) OOP_TO_OBJ (_gst_this_context_oop);
   context->flags |= MCF_IS_UNWIND_CONTEXT;
-  if UNCOMMON (send_block_value (numArgs))
+  if UNCOMMON (send_block_value (numArgs, 0))
     PRIM_FAILED;
   else
     PRIM_SUCCEEDED_RELOAD_IP;
@@ -2621,7 +2633,7 @@ primitive VMpr_BlockClosure_valueWithArguments 
[fail,reload_ip]
       for (i = 1; i <= numArgs; i++)
        PUSH_OOP (ARRAY_AT (oop2, i));
 
-      if UNCOMMON (send_block_value (numArgs))
+      if UNCOMMON (send_block_value (numArgs, 0))
        {
          POP_N_OOPS (numArgs);
          PUSH_OOP (oop2);
@@ -5026,7 +5038,7 @@ primitive VMpr_Behavior_primCompileIfError 
[fail,succeed,reload_ip]
          xfree (_gst_first_error_str);
          _gst_first_error_str = _gst_first_error_file = NULL;
          _gst_report_errors = oldReportErrors;
-         if (send_block_value (3))
+         if (send_block_value (3, 3))
            PRIM_FAILED;
          else
            PRIM_SUCCEEDED_RELOAD_IP;
diff --git a/libgst/vm.def b/libgst/vm.def
index bd00206..f85ef29 100644
--- a/libgst/vm.def
+++ b/libgst/vm.def
@@ -632,7 +632,7 @@ operation VALUE_SPECIAL ( rec -- rec ) {
   EXPORT_REGS ();
   if (UNCOMMON (IS_INT (rec))
       || UNCOMMON (OOP_CLASS (rec) != _gst_block_closure_class)
-      || UNCOMMON (send_block_value (0)))
+      || UNCOMMON (send_block_value (0, 0)))
     SEND_MESSAGE (_gst_builtin_selectors[VALUE_SPECIAL].symbol, 0);
 
   IMPORT_REGS ();
@@ -644,7 +644,7 @@ operation VALUE_COLON_SPECIAL ( rec blk_arg -- rec blk_arg 
) {
   EXPORT_REGS ();
   if (UNCOMMON (IS_INT (rec))
       || UNCOMMON (OOP_CLASS (rec) != _gst_block_closure_class)
-      || UNCOMMON (send_block_value (1)))
+      || UNCOMMON (send_block_value (1, 0)))
     SEND_MESSAGE (_gst_builtin_selectors[VALUE_COLON_SPECIAL].symbol, 1);
 
   IMPORT_REGS ();
diff --git a/tests/blocks.ok b/tests/blocks.ok
index 37f1d8d..6952443 100644
--- a/tests/blocks.ok
+++ b/tests/blocks.ok
@@ -72,3 +72,50 @@ returned value is 55
 Execution begins...
  error: return from a dead method context
 returned value is nil
+
+Execution begins...
+returned value is nil
+
+Execution begins...
+returned value is nil
+
+Execution begins...
+returned value is nil
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+ error: wrong number of arguments
+returned value is nil
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+returned value is 2
+
+Execution begins...
+returned value is 2
+
+Execution begins...
+ error: wrong number of arguments
+returned value is nil
+
+Execution begins...
+returned value is 1
+
+Execution begins...
+returned value is 2
+
+Execution begins...
+returned value is 3
diff --git a/tests/blocks.st b/tests/blocks.st
index c9641e8..8be78fc 100644
--- a/tests/blocks.st
+++ b/tests/blocks.st
@@ -153,3 +153,22 @@ Eval [
 Eval [ (nil blockTest11: 3) value ]    "should be invalid; we're returning to 
non-
                                         existent parent"
 
+"Various tests on #cull:cull:cull: and friends."
+Eval [   [] cull: 1     ]
+Eval [   [] cull: 1 cull: 2     ]
+Eval [   [] cull: 1 cull: 2 cull: 3    ]
+
+Eval [   [:a |a] cull: 1     ]
+Eval [   [:a |a] cull: 1 cull: 2     ]
+Eval [   [:a |a] cull: 1 cull: 2 cull: 3    ]
+
+Eval [   [:a :b |a] cull: 1     ]
+Eval [   [:a :b |a] cull: 1 cull: 2   ]
+Eval [   [:a :b |a] cull: 1 cull: 2 cull: 3   ]
+Eval [   [:a :b |b] cull: 1 cull: 2    ]
+Eval [   [:a :b |b] cull: 1 cull: 2 cull: 3    ]
+
+Eval [   [:a :b :c |a] cull: 1 cull: 2    ]
+Eval [   [:a :b :c |a] cull: 1 cull: 2 cull: 3    ]
+Eval [   [:a :b :c |b] cull: 1 cull: 2 cull: 3    ]
+Eval [   [:a :b :c |c] cull: 1 cull: 2 cull: 3    ]

reply via email to

[Prev in Thread] Current Thread [Next in Thread]