[PATCH] Garbage-collected CObjects

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] Garbage-collected CObjects

Paolo Bonzini-2
Having to use two mallocs and an #ensure: block just to wrap a function
that accepts two double* arguments is just awful.  But that's what the
Cairo bindings have to do!

So, here comes a way to use ByteArrays as an alternative backing storage
for CObjects.

-        | ox oy |
-       ox := CDouble value: aPoint x.
-       oy := CDouble value: aPoint y.
-       ^ [
-           block value:self value:ox value: oy
-       ] ensure: [
-           ox ifNotNil: [ :x | x free ].
-           oy ifNotNil: [ :y | y free ]].
+       ^block
+           value: self
+           value: (CDouble gcValue: aPoint x)
+           value: (CDouble gcValue: aPoint y)

Much nicer, and more efficient since the less you use finalization the
better.

However, it requires more care (it can crash badly if objects are moved
by a GC under the feet of C functions!), so it is accessed using special
#gcNew and #gcValue: methods, instead of changing the default instance
creation methods.

Incidentally, this would have been another way to solve the finalization
race problems with CStatStruct, that I fixed a while ago.

I took the opportunity to clean up a little the CObject docs.

Paolo

2008-05-06  Paolo Bonzini  <[hidden email]>

        * kernel/ByteArray.st: Rewrite memory access methods in terms
        of CObject.
        * kernel/CObject.st: Add support for ByteArrays as CObject storage.
        Add #= and #hash.
        * kernel/CStruct.st: Add #gcNew.
        * kernel/CType.st: Add #gcNew.
        * kernel/Object.st: Add #isCObject.

        * tests/cobjects.st: Add more tests.
        * tests/cobjects.ok: Update.

libgst:
2008-05-06  Paolo Bonzini  <[hidden email]>

        * libgst/callin.c: Adjust calls to COBJECT_NEW, COBJECT_VALUE,
        SET_COBJECT_VALUE.
        * libgst/cint.c: Likewise.  Add _gst_c_type_size.
        * libgst/cint.h: Declare _gst_c_type_size.
        * libgst/dict.c: Likewise.  Rename _gst_c_object_new to
        _gst_c_object_new_base, add new instance variable to CObject.
        Make CObject absolute in _gst_free_cobject.
        * libgst/dict.h: Adjust struct gst_cobject and rename
        prototype of _gst_c_object_new to _gst_c_object_new_base.
        * libgst/dict.inl: Add cobject_value, set_cobject_value,
        cobject_index_check.  Adjust COBJECT_NEW.  Rename COBJECT_VALUE_OBJ
        and SET_COBJECT_VALUE_OBJ to COBJECT_OFFSET_OBJ and
        SET_COBJECT_OFFSET_OBJ, respectively.
        * libgst/prims.def: Adjust calls to COBJECT_NEW, COBJECT_VALUE,
        SET_COBJECT_VALUE.  Add calls to cobject_index_check.  Handle
        derefAt:type: from a garbage-collected CObject specially, and
        otherwise preserve the base when casting a CObject.

packages/cairo:
2008-05-06  Paolo Bonzini  <[hidden email]>

        * CairoContext.st: Use GCed CStructs.
        * CairoTransform.st: Use GCed CStructs.

packages/sdl/libsdl:
2008-05-06  Paolo Bonzini  <[hidden email]>

        * Display.st: Use GCed CStructs.

diff --git a/NEWS b/NEWS
index a6fcc39..9397c6e 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,17 @@ List of user-visible changes in GNU Smalltalk
 
 NEWS FROM 3.0.2 TO 3.0a
 
+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
+    functions that call back to Smalltalk or otherwise may cause garbage
+    collections.  If it is, however, it is easier to use, faster and
+    more predictable than finalization.  As an added benefit,
+    garbage-collected CObjects accesses are bounds-checked.
+
+    Garbage-collected CObjects are created by sending #gcNew instead
+    of #new.
+
 o   ObjectMemory>>#snapshot and ObjectMemory>>#snapshot: return false in
     the instance of GNU Smalltalk that produced the snapshot, and
     true in the instance of GNU Smalltalk that was restored from the
diff --git a/TODO b/TODO
index aa61489..c41225a 100644
--- a/TODO
+++ b/TODO
@@ -9,11 +9,13 @@
 
 * Swazoo (done)
 
-* TwistedPools (almost there)
+* TwistedPools (done)
+
+** garbage-collected storage for CObjects (done)
 
 * more libraries
-** Cairo (must be documented, cross-platform checking)
-** SDL (likewise)
+** Cairo (missing cross-platform checking)
+** SDL (missing likewise)
 
 * less likely
 ** Expat
@@ -21,7 +23,6 @@
 ** 3D gnuplot?
 
 * maybe
-** allocate CStructs in ByteArrays?
 ** DBI refactoring and prepared statements support
 ** IPv6
 ** cookies and redirects for HTTPClient
diff --git a/doc/gst.texi b/doc/gst.texi
index 0316827..32465ee 100644
--- a/doc/gst.texi
+++ b/doc/gst.texi
@@ -1782,12 +1782,12 @@ effect that the VM can simply delay the releasing of the memory associated
 to the object, instead of being forced to waste memory even after
 finalization happens.
 
-An object must be explicitly marked as to be finalized @emph{every time the
-image is loaded}; that is, finalizability is not preserved by an
-image save.  This was done because in most cases finalization is
-used together with @code{CObject}s that would be stale when the image is
-loaded again, causing a segmentation violation as soon as they are accessed
-by the finalization method.
+An object must be explicitly marked as to be finalized @emph{every time
+the image is loaded}; that is, finalizability is not preserved by an
+image save.  This was done because in most cases finalization is used
+together with operating system resources that would be stale when the
+image is loaded again.  For @code{CObject}s, in particular, freeing them
+would cause a segmentation violation.
 @end defmethod
 
 @defmethod Object removeToBeFinalized
@@ -3431,14 +3431,39 @@ subclass called @code{CScalar}, which has subclasses called
 @code{C@var{mumble}}.  These subclasses can answer size and alignment
 information.
 
-Instances of @code{CObject} holds a pointer to a C type variable.  The
-variable can be allocated from Smalltalk by doing @code{@var{type}
-new}, where @var{type} is a @code{CType} subclass instance, or it
-may have been returned through the C callout mechanism as a return
-value.  Remember that @code{CObject} and its subclasses represent a
-pointer to a C object and as such provide the full range of operations
-supported by C pointers.
+Instances of @code{CObject} can hold a raw C pointer (for example in
+@code{malloc}ed heap)), or can delegate their storage to a @code{ByteArray}.
+In the latter case, the storage is automatically garbage collected when
+the @code{CObject} becomes dead, and the VM checks accesses to make sure
+they are in bounds.  On the other hand, the storage may move, and for this
+reason extra care must be put when using this kind of @code{CObject} with
+C routines that call back into Smalltalk, or that store the passed pointer
+somewhere.
 
+Instances of @code{CObject} can be created in many ways:
+@itemize
+@item creating an instance with @code{@var{class} new} initializes
+  the pointer to @code{NULL};
+
+@item doing @code{@var{type} new}, where @var{type} is a @code{CType}
+  subclass instance, allocates a new instance with @code{malloc}.
+
+@item doing @code{@var{type} gcNew}, where @var{type} is a @code{CType}
+  subclass instance, allocates a new instance backed by garbage-collected
+  storage.
+
+@end itemize
+
+@code{CStruct} and @code{CUnion} subclasses are special.  First,
+@code{new} allocates a new instance with @code{malloc} instead of initializing
+the pointer to @code{NULL}. Second, they support @code{gcNew} which
+creates a new instance backed by garbage-collected storage.
+
+@code{CObject}s created by the C callout mechanism are never backed by
+garbage-collected storage.
+
+@code{CObject} and its subclasses represent a pointer to a C object and
+as such provide the full range of operations supported by C pointers.
 For example, @code{+} @code{anInteger} which returns a CObject which is
 higher in memory by @code{anInteger} times the size of each item. There
 is also @code{-} which acts like @code{+} if it is given an
@@ -3449,37 +3474,35 @@ backward, by either 1 or @code{n} characters.  Only the pointer to the
 string is changed; the actual characters in the string remain untouched.
 
 CObjects can be divided into two families, scalars and non-scalars,
-just like C data types.  Scalars fetch a Smalltalk object when sent
-the @code{value} message, and change their value when sent the
-@code{value:} message.  Non-scalars do not support these two messages.
-
-@code{replaceWith:} @code{aString} replaces the string the instance
-points to with the new string.  Actually, it copies the bytes from the
-Smalltalk @code{String} instance aString into the C string object, and null
-terminates.  Be sure that the C string has enough room!  You can also
-use a Smalltalk @code{ByteArray} as the data source.
+just like C data types.  Scalars fetch a Smalltalk object when sent the
+@code{value} message, and change their value when sent the @code{value:}
+message.  Non-scalars do not support these two messages.  Non-scalars
+include instances of @code{CArray} and subclasses of @code{CStruct}
+and @code{CUnion} (but not @code{CPtr}).
 
-Non-scalars include instances of @code{CArray}, @code{CPtr} and
-subclasses of @code{CStruct} and @code{CUnion}.
-
-CPtrs and CArrays get their underlying element type through a
+@code{CPtr}s and @code{CArray}s get their underlying element type through a
 @code{CType} subclass instance which is associated with the
 @code{CArray} or @code{CPtr} instance.
 
-CPtr's also have @code{value} and @code{value:} which get or change the
-underlying value that's pointed to.  In practice, @code{value} dereferences
-the pointer.  CString is a subclass that answers a Smalltalk @code{String} when
-sent @code{value}, and automatically allocates storage to copy and
-null-terminate a Smalltalk @code{String} when sent @code{value:}.
-
-Note that a @code{CPtr} to @code{long} points to a place in memory where
-a pointer to long is stored.  In other words it is really a @code{long **}
-and must be dereferenced twice with @code{cPtr value value} to get the
-@code{long}.
+@code{CPtr}'s @code{value} and @code{value:} method get or change
+the underlying value that's pointed to.  @code{value} returns another
+@code{CObject} corresponding to the pointed value.  That's because, for
+example, a @code{CPtr} to @code{long} points to a place in memory where
+a pointer to long is stored.  It is really a @code{long **} and must be
+dereferenced twice with @code{cPtr value value} to get the @code{long}.
+
+@code{CString} is a subclass of @code{CPtr} that answers a Smalltalk
+@code{String} when sent @code{value}, and automatically allocates
+storage to copy and null-terminate a Smalltalk @code{String} when sent
+@code{value:}.  @code{replaceWith:} replaces the string the instance
+points to with a new string or @code{ByteArray}, passed as the argument.
+Actually, it copies the bytes from the Smalltalk @code{String} instance
+aString into the same buffer already pointed to by the @code{CString},
+with a null terminator.
 
 Finally, there are @code{CStruct} and @code{CUnion}, which are abstract
 subclasses of @code{CObject}@footnote{Actually they have a common superclass
-named @code{CCompound}.}.  In the following I will refer to CStruct, but the
+named @code{CCompound}.}.  The following will refer to CStruct, but the
 same considerations apply to CUnion as well, with the only difference that
 CUnions of course implement the semantics of a C union.
 
diff --git a/kernel/ByteArray.st b/kernel/ByteArray.st
index ab4a236..946737d 100644
--- a/kernel/ByteArray.st
+++ b/kernel/ByteArray.st
@@ -110,7 +110,6 @@ a String''s elements are characters.'>
  ^self
     type: 9
     at: index
-    size: CPtrSize - 1
     ]
 
     charAt: index [
@@ -122,7 +121,6 @@ a String''s elements are characters.'>
  ^self
     type: 0
     at: index
-    size: 0
     ]
 
     unsignedCharAt: index [
@@ -134,7 +132,6 @@ a String''s elements are characters.'>
  ^self
     type: 1
     at: index
-    size: 0
     ]
 
     ucharAt: index [
@@ -146,7 +143,6 @@ a String''s elements are characters.'>
  ^self
     type: 1
     at: index
-    size: 0
     ]
 
     shortAt: index [
@@ -157,7 +153,6 @@ a String''s elements are characters.'>
  ^self
     type: 2
     at: index
-    size: CShortSize - 1
     ]
 
     unsignedShortAt: index [
@@ -168,7 +163,6 @@ a String''s elements are characters.'>
  ^self
     type: 3
     at: index
-    size: CShortSize - 1
     ]
 
     ushortAt: index [
@@ -179,7 +173,6 @@ a String''s elements are characters.'>
  ^self
     type: 3
     at: index
-    size: CShortSize - 1
     ]
 
     longAt: index [
@@ -190,7 +183,6 @@ a String''s elements are characters.'>
  ^self
     type: 4
     at: index
-    size: CLongSize - 1
     ]
 
     unsignedLongAt: index [
@@ -201,7 +193,6 @@ a String''s elements are characters.'>
  ^self
     type: 5
     at: index
-    size: CLongSize - 1
     ]
 
     ulongAt: index [
@@ -212,7 +203,6 @@ a String''s elements are characters.'>
  ^self
     type: 5
     at: index
-    size: CLongSize - 1
     ]
 
     intAt: index [
@@ -223,7 +213,6 @@ a String''s elements are characters.'>
  ^self
     type: 10
     at: index
-    size: CIntSize - 1
     ]
 
     unsignedIntAt: index [
@@ -234,7 +223,6 @@ a String''s elements are characters.'>
  ^self
     type: 11
     at: index
-    size: CIntSize - 1
     ]
 
     uintAt: index [
@@ -245,7 +233,6 @@ a String''s elements are characters.'>
  ^self
     type: 11
     at: index
-    size: CIntSize - 1
     ]
 
     floatAt: index [
@@ -256,7 +243,6 @@ a String''s elements are characters.'>
  ^self
     type: 6
     at: index
-    size: CFloatSize - 1
     ]
 
     doubleAt: index [
@@ -267,7 +253,6 @@ a String''s elements are characters.'>
  ^self
     type: 7
     at: index
-    size: CDoubleSize - 1
     ]
 
     longDoubleAt: index [
@@ -278,7 +263,6 @@ a String''s elements are characters.'>
  ^self
     type: 12
     at: index
-    size: CLongDoubleSize - 1
     ]
 
     stringAt: index [
@@ -289,7 +273,6 @@ a String''s elements are characters.'>
  ^self
     type: 8
     at: index
-    size: CPtrSize - 1
     ]
 
     objectAt: index put: value [
@@ -302,7 +285,6 @@ a String''s elements are characters.'>
     type: 9
     at: index
     put: value
-    size: CPtrSize - 1
     ]
 
     charAt: index put: value [
@@ -316,7 +298,6 @@ a String''s elements are characters.'>
     type: 0
     at: index
     put: value
-    size: 0
     ]
 
     unsignedCharAt: index put: value [
@@ -330,7 +311,6 @@ a String''s elements are characters.'>
     type: 1
     at: index
     put: value
-    size: 0
     ]
 
     ucharAt: index put: value [
@@ -344,7 +324,6 @@ a String''s elements are characters.'>
     type: 1
     at: index
     put: value
-    size: 0
     ]
 
     shortAt: index put: value [
@@ -357,7 +336,6 @@ a String''s elements are characters.'>
     type: 2
     at: index
     put: value
-    size: CShortSize - 1
     ]
 
     unsignedShortAt: index put: value [
@@ -370,7 +348,6 @@ a String''s elements are characters.'>
     type: 3
     at: index
     put: value
-    size: CShortSize - 1
     ]
 
     ushortAt: index put: value [
@@ -383,7 +360,6 @@ a String''s elements are characters.'>
     type: 3
     at: index
     put: value
-    size: CShortSize - 1
     ]
 
     longAt: index put: value [
@@ -396,7 +372,6 @@ a String''s elements are characters.'>
     type: 4
     at: index
     put: value
-    size: CLongSize - 1
     ]
 
     unsignedLongAt: index put: value [
@@ -409,7 +384,6 @@ a String''s elements are characters.'>
     type: 5
     at: index
     put: value
-    size: CLongSize - 1
     ]
 
     ulongAt: index put: value [
@@ -422,7 +396,6 @@ a String''s elements are characters.'>
     type: 5
     at: index
     put: value
-    size: CLongSize - 1
     ]
 
     intAt: index put: value [
@@ -435,7 +408,6 @@ a String''s elements are characters.'>
     type: 10
     at: index
     put: value
-    size: CIntSize - 1
     ]
 
     unsignedIntAt: index put: value [
@@ -448,7 +420,6 @@ a String''s elements are characters.'>
     type: 11
     at: index
     put: value
-    size: CIntSize - 1
     ]
 
     uintAt: index put: value [
@@ -461,7 +432,6 @@ a String''s elements are characters.'>
     type: 11
     at: index
     put: value
-    size: CIntSize - 1
     ]
 
     floatAt: index put: value [
@@ -474,7 +444,6 @@ a String''s elements are characters.'>
     type: 6
     at: index
     put: value
-    size: CFloatSize - 1
     ]
 
     doubleAt: index put: value [
@@ -487,7 +456,6 @@ a String''s elements are characters.'>
     type: 7
     at: index
     put: value
-    size: CDoubleSize - 1
     ]
 
     longDoubleAt: index put: value [
@@ -500,7 +468,6 @@ a String''s elements are characters.'>
     type: 12
     at: index
     put: value
-    size: CLongDoubleSize - 1
     ]
 
     stringAt: index put: value [
@@ -515,7 +482,6 @@ a String''s elements are characters.'>
     type: 8
     at: index
     put: value
-    size: CPtrSize - 1
     ]
 
     growSize [
@@ -527,36 +493,21 @@ a String''s elements are characters.'>
  ^self size
     ]
 
-    type: type at: index size: size [
+    type: type at: index [
  "Private - Use Memory class to access in the receiver a value with the
  given type."
 
  <category: 'private'>
- | offset |
- index < 1
-    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
- index > (self basicSize - size)
-    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
- offset := index + (CLongSize * 2 - 1). "impl. dependent"
- ^Memory type: type at: (ObjectMemory addressOf: self) + offset
+ ^(CObject new storage: self) at: index - 1 type: type
     ]
 
-    type: type at: index put: value size: size [
+    type: type at: index put: value [
  "Private - Use Memory class to write to the receiver a value with the
  given type."
 
  <category: 'private'>
- | offset |
  self isReadOnly ifTrue: [^SystemExceptions.ReadOnlyObject signal].
- index < 1
-    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
- index > (self basicSize - size)
-    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
- offset := index + (CLongSize * 2 - 1). "impl. dependent"
- ^Memory
-    type: type
-    at: (ObjectMemory addressOf: self) + offset
-    put: value
+ ^(CObject new storage: self) at: index - 1 put: value type: type
     ]
 
     byteAt: index [
diff --git a/kernel/CObject.st b/kernel/CObject.st
index 1d1a24f..3648825 100644
--- a/kernel/CObject.st
+++ b/kernel/CObject.st
@@ -33,7 +33,7 @@
 
 
 Object subclass: CObject [
-    | type |
+    | type storage |
     
     <shape: #word>
     <import: CSymbols>
@@ -58,6 +58,31 @@ into their corresponding C values for use in external routines.'>
     yourself
     ]
 
+    CObject class >> alloc: nBytes type: cTypeObject [
+ "Allocate nBytes bytes and return a CObject of the given type"
+
+ <category: 'primitive allocation'>
+ <primitive: VMpr_CObject_allocType>
+ nBytes isInteger
+    ifFalse: [^SystemExceptions.WrongClass signalOn: nBytes mustBe: SmallInteger].
+ ^SystemExceptions.WrongClass signalOn: cTypeObject mustBe: CType
+    ]
+
+    CObject class >> gcAlloc: nBytes type: cTypeObject [
+ "Allocate nBytes bytes and return a CObject of the given type"
+
+ <category: 'primitive allocation'>
+ | class |
+ class := cTypeObject isNil
+    ifTrue: [ self ]
+    ifFalse: [ cTypeObject cObjectType ].
+
+ ^(class new)
+    type: cTypeObject;
+    storage: (ByteArray new: nBytes);
+    yourself
+    ]
+
     CObject class >> alloc: nBytes [
  "Allocate nBytes bytes and return an instance of the receiver"
 
@@ -65,21 +90,25 @@ into their corresponding C values for use in external routines.'>
  ^self alloc: nBytes type: nil
     ]
 
-    CObject class >> new: nBytes [
+    CObject class >> gcAlloc: nBytes [
  "Allocate nBytes bytes and return an instance of the receiver"
 
  <category: 'instance creation'>
- ^self alloc: nBytes type: nil
+ ^self gcAlloc: nBytes type: nil
     ]
 
-    CObject class >> alloc: nBytes type: cTypeObject [
- "Allocate nBytes bytes and return a CObject of the given type"
+    CObject class >> gcNew: nBytes [
+ "Allocate nBytes bytes and return an instance of the receiver"
 
  <category: 'instance creation'>
- <primitive: VMpr_CObject_allocType>
- nBytes isInteger
-    ifFalse: [^SystemExceptions.WrongClass signalOn: nBytes mustBe: SmallInteger].
- ^SystemExceptions.WrongClass signalOn: cTypeObject mustBe: CType
+ ^self gcAlloc: nBytes type: nil
+    ]
+
+    CObject class >> new: nBytes [
+ "Allocate nBytes bytes and return an instance of the receiver"
+
+ <category: 'instance creation'>
+ ^self alloc: nBytes type: nil
     ]
 
     CObject class >> address: anInteger [
@@ -112,6 +141,24 @@ into their corresponding C values for use in external routines.'>
  ^nil
     ]
 
+    = anObject [
+ "Return true if the receiver and aCObject are equal."
+
+ <category: 'basic'>
+ ^self class == anObject class and: [
+    self type = anObject type and: [
+    self storage == anObject storage and: [
+    self address = anObject address ]]]
+    ]
+
+    hash [
+ "Return a hash value for anObject."
+
+ <category: 'basic'>
+ ^self type hash
+    bitXor: (self storage identityHash * self sizeof + self address)
+    ]
+
     finalize [
  "To make the VM call this, use #addToBeFinalized. It frees
  automatically any memory pointed to by the CObject. It is not
@@ -179,6 +226,11 @@ into their corresponding C values for use in external routines.'>
  ^aValue
     ]
 
+    isCObject [
+ <category: 'testing functionality'>
+ ^true
+    ]
+
     incr [
  "Adjust the pointer by sizeof(dereferencedType) bytes up (i.e. ++receiver)"
 
@@ -265,10 +317,37 @@ into their corresponding C values for use in external routines.'>
  ^type
     ]
 
+    isAbsolute [
+ "Answer whether the object points into a garbage-collected Smalltalk
+ storage, or it is an absolute address."
+
+ <category: 'accessing'>
+ ^storage isNil
+    ]
+
+    storage [
+ "Answer the storage that the receiver is pointing into, or nil
+ if the address is absolute."
+
+ <category: 'accessing'>
+ ^storage
+    ]
+
+    storage: anObject [
+ "Change the receiver to point to the storage of anObject."
+
+ <category: 'accessing'>
+ storage := anObject.
+    ]
+
     address [
- "Answer the address the receiver is pointing to."
+ "Answer the address the receiver is pointing to.  The address can
+ be absolute if the storage is nil, or relative to the Smalltalk
+ object in #storage.  In this case, an address of 0 corresponds to
+ the first instance variable."
 
  <category: 'accessing'>
+ <primitive: VMpr_CObject_address>
  ^self basicAt: self basicSize
     ]
 
@@ -276,7 +355,8 @@ into their corresponding C values for use in external routines.'>
  "Set the receiver to point to the passed address, anInteger"
 
  <category: 'accessing'>
- self basicAt: self basicSize put: anInteger
+ <primitive: VMpr_CObject_addressColon>
+ SystemExceptions.WrongClass signalOn: anInteger mustBe: Integer
     ]
 
     printOn: aStream [
@@ -285,9 +365,15 @@ into their corresponding C values for use in external routines.'>
  <category: 'accessing'>
  aStream
     print: self class;
-    nextPut: $(;
-    nextPutAll: (self address printStringRadix: 16);
-    nextPut: $)
+    nextPut: $(.
+
+ self isAbsolute
+    ifTrue: [ aStream nextPutAll: (self address printStringRadix: 16) ]
+    ifFalse: [
+ self storage do: [ :each | aStream print: each; space ].
+ aStream nextPutAll: '@ '; print: self address ].
+
+ aStream nextPut: $)
     ]
 
     type: aCType [
@@ -332,6 +418,10 @@ into their corresponding C values for use in external routines.'>
  byteOffset isInteger
     ifFalse:
  [^SystemExceptions.WrongClass signalOn: byteOffset mustBe: SmallInteger].
+ (self isAbsolute not and: [ aType isInteger ]) ifTrue: [
+    ^SystemExceptions.InvalidArgument signalOn: self address + byteOffset
+ reason: 'offset out of range' ].
+
  ^SystemExceptions.WrongClass signalOn: aType
     ]
 
@@ -342,6 +432,11 @@ into their corresponding C values for use in external routines.'>
  <category: 'C data access'>
  | type |
  <primitive: VMpr_CObject_atPut>
+
+ (self isAbsolute not and: [ aValue isCObject not ]) ifTrue: [
+    ^SystemExceptions.InvalidArgument signalOn: self address + byteOffset
+ reason: 'offset out of range' ].
+
  type := aValue cObjStoredType.
 
  "Attempt to store something meaningful from another CObject"
@@ -407,6 +502,17 @@ CObject subclass: CScalar [
  ^cObject
     ]
 
+    CScalar class >> gcValue: anObject [
+ "Answer a newly allocated CObject containing the passed value,
+ anObject, in garbage-collected storage."
+
+ <category: 'instance creation'>
+ | cObject |
+ cObject := self type new.
+ cObject value: anObject.
+ ^cObject
+    ]
+
     CScalar class >> type [
  "Answer a CType for the receiver---for example, CByteType if
  the receiver is CByte."
diff --git a/kernel/CStruct.st b/kernel/CStruct.st
index 53db2bf..fd9964f 100644
--- a/kernel/CStruct.st
+++ b/kernel/CStruct.st
@@ -43,6 +43,14 @@ CObject subclass: CCompound [
 
     ]
 
+    CCompound class >> gcNew [
+ "Allocate a new instance of the receiver, backed by garbage-collected
+ storage."
+
+ <category: 'instance creation'>
+ ^self gcAlloc: self sizeof
+    ]
+
     CCompound class >> new [
  "Allocate a new instance of the receiver. To free the memory after
  GC, remember to call #addToBeFinalized."
diff --git a/kernel/CType.st b/kernel/CType.st
index 2cae008..cef271f 100644
--- a/kernel/CType.st
+++ b/kernel/CType.st
@@ -136,6 +136,15 @@ elements.'>
  structureType == #ptr ifTrue: [^CPtrCType from: type]
     ]
 
+    gcNew [
+ "Allocate a new CObject with the type (class) identified by the receiver.
+ The object is movable in memory, but on the other hand it is
+ garbage-collected automatically."
+
+ <category: 'C instance creation'>
+ ^CObject gcAlloc: self sizeof type: self
+    ]
+
     new [
  "Allocate a new CObject with the type (class) identified by the receiver.
  It is the caller's responsibility to free the memory allocated for it."
diff --git a/kernel/Object.st b/kernel/Object.st
index 11095b4..90e0bff 100644
--- a/kernel/Object.st
+++ b/kernel/Object.st
@@ -162,6 +162,11 @@ All classes in the system are subclasses of me.'>
  ^notNilBlock value: self
     ]
 
+    isCObject [
+ <category: 'testing functionality'>
+ ^false
+    ]
+
     isString [
  <category: 'testing functionality'>
  ^false
diff --git a/libgst/callin.c b/libgst/callin.c
index 4cbd9af..29006d3 100644
--- a/libgst/callin.c
+++ b/libgst/callin.c
@@ -234,7 +234,8 @@ _gst_va_msg_sendf (PTR resultPtr,
           break;
 
         case 'C':
-          args[++i] = COBJECT_NEW (va_arg (ap, PTR));
+          args[++i] = COBJECT_NEW (va_arg (ap, PTR), _gst_nil_oop,
+   _gst_c_object_class);
   INC_ADD_OOP (args[i]);
           break;
 
@@ -259,8 +260,7 @@ _gst_va_msg_sendf (PTR resultPtr,
     ctype = _gst_type_name_to_oop (va_arg (ap, const char *));
     INC_ADD_OOP (ctype);
 
-    args[++i] =
-      _gst_c_object_new (va_arg (ap, PTR), ctype, _gst_nil_oop);
+    args[++i] = COBJECT_NEW (va_arg (ap, PTR), ctype, _gst_nil_oop);
 
     INC_ADD_OOP (args[i]);
           }
@@ -271,8 +271,7 @@ _gst_va_msg_sendf (PTR resultPtr,
           {
         OOP ctype;
         ctype = va_arg (ap, OOP);
-        args[++i] =
-          _gst_c_object_new (va_arg (ap, PTR), ctype, _gst_nil_oop);
+        args[++i] = COBJECT_NEW (va_arg (ap, PTR), ctype, _gst_nil_oop);
 
     INC_ADD_OOP (args[i]);
           }
@@ -307,7 +306,7 @@ _gst_va_msg_sendf (PTR resultPtr,
 
  case 'C':
   *(PTR *) resultPtr =
-    IS_NIL (result) ? NULL : COBJECT_VALUE (result);
+    IS_NIL (result) ? NULL : cobject_value (result);
   break;
 
  case 's':
@@ -588,7 +587,7 @@ _gst_c_object_to_oop (PTR co)
   if (co == NULL)
     return (_gst_nil_oop);
   else
-    return (INC_ADD_OOP (COBJECT_NEW (co)));
+    return (INC_ADD_OOP (COBJECT_NEW (co, _gst_nil_oop, _gst_c_object_class)));
 }
 
 void
@@ -597,7 +596,7 @@ _gst_set_c_object (OOP oop, PTR co)
   if (!_gst_smalltalk_initialized)
     _gst_initialize (NULL, NULL, GST_NO_TTY);
 
-  SET_COBJECT_VALUE(oop, co);
+  set_cobject_value (oop, co);
 }
 
 
@@ -630,7 +629,7 @@ _gst_oop_to_c (OOP oop)
     return (0);
 
   else if (is_a_kind_of (OOP_CLASS (oop), _gst_c_object_class))
-    return ((long) COBJECT_VALUE (oop));
+    return ((long) cobject_value (oop));
 
   else
     return (0);
@@ -758,7 +757,7 @@ _gst_oop_to_c_object (OOP oop)
   if (IS_NIL (oop))
     return (NULL);
   else
-    return (COBJECT_VALUE (oop));
+    return (cobject_value (oop));
 }
 
 OOP
diff --git a/libgst/cint.c b/libgst/cint.c
index 2f7bfc0..c4ed5d8 100644
--- a/libgst/cint.c
+++ b/libgst/cint.c
@@ -652,6 +652,63 @@ lookup_function (const char *funcName)
 }
 
 
+int
+_gst_c_type_size (int type)
+{
+  switch (type)
+    {
+    case CDATA_CHAR:
+      return sizeof (char);
+    case CDATA_UCHAR:
+      return sizeof (unsigned char);
+
+    case CDATA_SHORT:
+      return sizeof (short);
+    case CDATA_USHORT:
+      return sizeof (unsigned short);
+
+    case CDATA_INT:
+      return sizeof (int);
+    case CDATA_UINT:
+      return sizeof (unsigned int);
+
+    case CDATA_LONG:
+      return sizeof (long);
+    case CDATA_ULONG:
+      return sizeof (unsigned long);
+
+    case CDATA_FLOAT:
+      return sizeof (float);
+    case CDATA_DOUBLE:
+      return sizeof (double);
+    case CDATA_LONG_DOUBLE:
+      return sizeof (long double);
+
+    case CDATA_OOP:
+      return sizeof (OOP);
+
+    case CDATA_WCHAR:
+      return sizeof (wchar_t);
+
+    case CDATA_WSTRING:
+      return sizeof (wchar_t *);
+
+    case CDATA_STRING:
+    case CDATA_STRING_OUT:
+    case CDATA_SYMBOL:
+    case CDATA_BYTEARRAY:
+    case CDATA_BYTEARRAY_OUT:
+    case CDATA_SYMBOL_OUT:
+      return sizeof (char *);
+
+    case CDATA_COBJECT:
+      return sizeof (void *);
+
+    case CDATA_COBJECT_PTR:
+      return sizeof (void **);
+    }
+}
+
 OOP
 _gst_invoke_croutine (OOP cFuncOOP,
       OOP receiver,
@@ -671,7 +728,7 @@ _gst_invoke_croutine (OOP cFuncOOP,
   if (IS_NIL (desc->cFunction))
     return (NULL);
 
-  c_func_cur = (cfunc_info *) COBJECT_VALUE (desc->cFunction);
+  c_func_cur = (cfunc_info *) cobject_value (desc->cFunction);
   if (!c_func_cur)
     return (NULL);
 
@@ -752,7 +809,7 @@ _gst_invoke_croutine (OOP cFuncOOP,
         switch (arg->cType)
           {
           case CDATA_COBJECT_PTR:
-    SET_COBJECT_VALUE (arg->oop, arg->u.cObjectPtrVal.ptrVal);
+    set_cobject_value (arg->oop, arg->u.cObjectPtrVal.ptrVal);
     continue;
 
           case CDATA_WSTRING_OUT:
@@ -1053,13 +1110,13 @@ push_smalltalk_obj (OOP oop,
   /* Set up an indirect pointer to protect against the OOP
      moving during the call-out.  */
   cp->u.cObjectPtrVal.pPtrVal = &cp->u.cObjectPtrVal.ptrVal;
-  cp->u.cObjectPtrVal.ptrVal = COBJECT_VALUE (oop);
+  cp->u.cObjectPtrVal.ptrVal = cobject_value (oop);
   cp->oop = oop;
   SET_TYPE (&ffi_type_pointer);
   return;
 
  case CDATA_COBJECT:
-  cp->u.ptrVal = COBJECT_VALUE (oop);
+  cp->u.ptrVal = cobject_value (oop);
   SET_TYPE (&ffi_type_pointer);
   return;
  }
@@ -1162,8 +1219,8 @@ c_to_smalltalk (cparam *result, OOP returnTypeOOP)
  {
   if (IS_INT (returnTypeOOP))
     returnTypeOOP = _gst_nil_oop;
-  resultOOP = _gst_c_object_new (result->u.ptrVal, returnTypeOOP,
- _gst_c_object_class);
+  resultOOP = COBJECT_NEW (result->u.ptrVal, returnTypeOOP,
+   _gst_c_object_class);
  }
       else if (returnType == CDATA_STRING || returnType == CDATA_STRING_OUT)
  {
@@ -1244,7 +1301,7 @@ _gst_make_descriptor (OOP classOOP,
      OOPs */
   incPtr = INC_SAVE_POINTER ();
 
-  cFunction = COBJECT_NEW (cfi);
+  cFunction = COBJECT_NEW (cfi, _gst_nil_oop, _gst_c_object_class);
   INC_ADD_OOP (cFunction);
 
   cFunctionName = _gst_string_new (funcName);
diff --git a/libgst/cint.h b/libgst/cint.h
index 09bed43..db92e68 100644
--- a/libgst/cint.h
+++ b/libgst/cint.h
@@ -118,6 +118,9 @@ typedef struct gst_cfunc_descriptor
 }
  *gst_cfunc_descriptor;
 
+/* Returns the size of an object passed to a C routine with type TYPE.  */
+extern int _gst_c_type_size (int type);
+
 /* Invokes a C routine.  Arguments passed from Smalltalk are stored starting
    from ARGS, and the object to which the message that called-out was
    sent is RECEIVER.  CFUNCOOP is the C function descriptor used
diff --git a/libgst/dict.c b/libgst/dict.c
index 2611ad9..7878822 100644
--- a/libgst/dict.c
+++ b/libgst/dict.c
@@ -707,8 +707,8 @@ static const class_definition class_info[] = {
    "SecurityPolicy", "dictionary owner", NULL, NULL },
 
   {&_gst_c_object_class, &_gst_object_class,
-   ISP_ULONG, true, 1, /* leave this this way */
-   "CObject", "type", NULL, "CSymbols" },
+   ISP_ULONG, true, 2,
+   "CObject", "type storage", NULL, "CSymbols" },
 
   {&_gst_c_type_class, &_gst_object_class,
    ISP_FIXED, true, 1,
@@ -2080,9 +2080,10 @@ _gst_message_new_args (OOP selectorOOP,
 }
 
 OOP
-_gst_c_object_new (PTR cObjPtr,
-   OOP typeOOP,
-   OOP defaultClassOOP)
+_gst_c_object_new_base (OOP baseOOP,
+        uintptr_t cObjOfs,
+        OOP typeOOP,
+        OOP defaultClassOOP)
 {
   gst_cobject cObject;
   gst_ctype cType;
@@ -2099,7 +2100,8 @@ _gst_c_object_new (PTR cObjPtr,
     
   cObject = (gst_cobject) new_instance_with (classOOP, 1, &cObjectOOP);
   cObject->type = typeOOP;
-  SET_COBJECT_VALUE_OBJ (cObject, cObjPtr);
+  cObject->storage = baseOOP;
+  SET_COBJECT_OFFSET_OBJ (cObject, cObjOfs);
 
   return (cObjectOOP);
 }
@@ -2111,10 +2113,13 @@ _gst_free_cobject (OOP cObjOOP)
   gst_cobject cObject;
 
   cObject = (gst_cobject) OOP_TO_OBJ (cObjOOP);
-  xfree ((PTR) COBJECT_VALUE_OBJ (cObject));
+  if (!IS_NIL (cObject->storage))
+    cObject->storage = _gst_nil_oop;
+  else
+    xfree ((PTR) COBJECT_OFFSET_OBJ (cObject));
 
-  /* at least make it not point to falsely valid storage */
-  SET_COBJECT_VALUE_OBJ (cObject, NULL);
+  /* make it not point to falsely valid storage */
+  SET_COBJECT_OFFSET_OBJ (cObject, NULL);
 }
 
 void
diff --git a/libgst/dict.h b/libgst/dict.h
index d79fc78..ba58fca 100644
--- a/libgst/dict.h
+++ b/libgst/dict.h
@@ -291,6 +291,7 @@ typedef struct gst_cobject
 {
   OBJ_HEADER;
   OOP type;
+  OOP storage;
 }
  *gst_cobject;
 
@@ -516,12 +517,14 @@ extern OOP _gst_shared_pool_dictionary (OOP class_oop)
   ATTRIBUTE_PURE
   ATTRIBUTE_HIDDEN;
 
-/* Creates a new CObject pointing to cObjPtr, extracting the class
+/* Creates a new CObject pointing to cObjOfs bytes in BASEOOP (or
+   at the absolute address cObjOfs if BASEOOP is NULL), extracting the class
    to be instantiated from the CType, TYPEOOP, or using the provided
    class if TYPEOOP is nil.  */
-extern OOP _gst_c_object_new (PTR cObjPtr,
-      OOP typeOOP,
-      OOP defaultClassOOP)
+extern OOP _gst_c_object_new_base (OOP baseOOP,
+           uintptr_t cObjOfs,
+           OOP typeOOP,
+           OOP defaultClassOOP)
   ATTRIBUTE_HIDDEN;
 
 /* Creates a new String with LEN indexed instance variables.  */
diff --git a/libgst/dict.inl b/libgst/dict.inl
index b46d2bf..85a30af 100644
--- a/libgst/dict.inl
+++ b/libgst/dict.inl
@@ -198,6 +198,17 @@ static inline OOP floate_new (double f);
    problems.  */
 static inline OOP floatq_new (long double f);
 
+/* Returns the address of the data stored in a CObject.  */
+static inline PTR cobject_value (OOP oop);
+
+/* Sets the address of the data stored in a CObject.  */
+static inline void set_cobject_value (OOP oop, PTR val);
+
+/* Return whether the address of the data stored in a CObject, offsetted
+   by OFFSET bytes, is still in bounds.  */
+static inline mst_Boolean cobject_index_check (OOP oop, intptr_t offset,
+       size_t size);
+
 /* Answer true if OOP is a SmallInteger or a LargeInteger of an
    appropriate size.  */
 static inline mst_Boolean is_c_int_32 (OOP oop);
@@ -304,26 +315,19 @@ static inline int64_t to_c_int_64 (OOP oop);
   (((gst_message)OOP_TO_OBJ(messageOOP))->args)
 
 /* Answer a new CObject pointing to COBJPTR.  */
-#define COBJECT_NEW(cObjPtr) \
-  (_gst_c_object_new(cObjPtr, _gst_nil_oop, _gst_c_object_class))
+#define COBJECT_NEW(cObjPtr, typeOOP, defaultClassOOP) \
+  (_gst_c_object_new_base(_gst_nil_oop, (uintptr_t) cObjPtr, \
+          typeOOP, defaultClassOOP))
 
-/* Answer the void * extracted from a CObject, COBJ (*not* an OOP,
+/* Answer the offset component of the a CObject, COBJ (*not* an OOP,
    but an object pointer).  */
-#define COBJECT_VALUE_OBJ(cObj) \
-  ( ((PTR *) cObj) [TO_INT(((gst_object)cObj)->objSize) - 1])
+#define COBJECT_OFFSET_OBJ(cObj) \
+  ( ((uintptr_t *) cObj) [TO_INT(cObj->objSize) - 1])
 
-/* Sets to VALUE the void * pointed to by the CObject, COBJ (*not* an
+/* Sets to VALUE the offset component of the CObject, COBJ (*not* an
    OOP, but an object pointer).  */
-#define SET_COBJECT_VALUE_OBJ(cObj, value) \
-  ( ((PTR *) cObj) [TO_INT(((gst_object)cObj)->objSize) - 1] = (PTR)(value))
-
-/* Sets to VALUE the void * pointed to by the CObject, COBJOOP.  */
-#define COBJECT_VALUE(cObjOOP) \
-  COBJECT_VALUE_OBJ(OOP_TO_OBJ(cObjOOP))
-
-/* Sets to VALUE the void * pointed to by the CObject, COBJOOP.  */
-#define SET_COBJECT_VALUE(cObjOOP, value) \
-  SET_COBJECT_VALUE_OBJ(OOP_TO_OBJ(cObjOOP), value)
+#define SET_COBJECT_OFFSET_OBJ(cObj, value) \
+  ( ((uintptr_t *) cObj) [TO_INT(cObj->objSize) - 1] = (uintptr_t)(value))
 
 /* Answer the superclass of the Behavior, CLASS_OOP.  */
 #define SUPERCLASS(class_oop) \
@@ -1484,3 +1488,48 @@ from_c_uint_64 (uint64_t ui)
 
   return (oop);
 }
+
+static inline PTR
+cobject_value (OOP oop)
+{
+  gst_cobject cObj = (gst_cobject) OOP_TO_OBJ (oop);
+  if (IS_NIL (cObj->storage))
+    return (PTR) COBJECT_OFFSET_OBJ (cObj);
+  else
+    {
+      gst_uchar *baseAddr = ((gst_byte_array) OOP_TO_OBJ (cObj->storage))->bytes;
+      return (PTR) (baseAddr + COBJECT_OFFSET_OBJ (cObj));
+    }
+}
+
+/* Sets the address of the data stored in a CObject.  */
+static inline void
+set_cobject_value (OOP oop, PTR val)
+{
+  gst_cobject cObj = (gst_cobject) OOP_TO_OBJ (oop);
+  cObj->storage = _gst_nil_oop;
+  SET_COBJECT_OFFSET_OBJ (cObj, (uintptr_t) val);
+}
+  
+
+/* Return whether the address of the data stored in a CObject, offsetted
+   by OFFSET bytes, is still in bounds.  */
+static inline mst_Boolean cobject_index_check (OOP oop, intptr_t offset,
+       size_t size)
+{
+  gst_cobject cObj = (gst_cobject) OOP_TO_OBJ (oop);
+  OOP baseOOP = cObj->storage;
+  intptr_t maxOffset;
+  if (IS_NIL (baseOOP))
+    return true;
+  
+  offset += COBJECT_OFFSET_OBJ (cObj);
+  if (offset < 0)
+    return false;
+
+  maxOffset = SIZE_TO_BYTES (NUM_WORDS (OOP_TO_OBJ (baseOOP)));
+  if (baseOOP->flags & F_BYTE)
+    maxOffset -= (baseOOP->flags & EMPTY_BYTES);
+
+  return (offset + size - 1 < maxOffset);
+}
diff --git a/libgst/prims.def b/libgst/prims.def
index ce491bd..e896c53 100644
--- a/libgst/prims.def
+++ b/libgst/prims.def
@@ -3913,7 +3913,7 @@ primitive VMpr_CObject_allocType [succeed,fail]
     {
       intptr_t arg2 = TO_INT (oop2);
       PTR ptr = xmalloc (arg2);
-      OOP cObjectOOP = _gst_c_object_new (ptr, oop1, oop3);
+      OOP cObjectOOP = COBJECT_NEW (ptr, oop1, oop3);
 
       POP_N_OOPS (2);
       SET_STACKTOP (cObjectOOP);
@@ -4394,7 +4394,7 @@ primitive VMpr_CObject_at :
 
   oop3 = POP_OOP ();
   oop2 = POP_OOP ();
-  oop1 = POP_OOP ();
+  oop1 = STACKTOP ();
   if (IS_INT (oop2)
       && ((IS_INT (oop3) && id == prim_id (VMpr_CObject_at))
   || is_a_kind_of (OOP_CLASS (oop3), _gst_c_type_class)))
@@ -4404,40 +4404,41 @@ primitive VMpr_CObject_at :
       arg2 = TO_INT (oop2);
       if (IS_INT (oop3))
  { /* int type spec means a scalar type */
-  intptr_t arg3;
-  addr = COBJECT_VALUE (oop1);
-          addr += arg2; /* compute effective address */
-  arg3 = TO_INT (oop3);
+  intptr_t arg3 = TO_INT (oop3);
+
+  if (!cobject_index_check (oop1, arg2, _gst_c_type_size (arg3)))
+    goto fail;
 
+  addr = ((char *) cobject_value (oop1)) + arg2;
   switch (arg3)
     {
     case CDATA_CHAR:
     case CDATA_UCHAR:
-      PUSH_OOP (CHAR_OOP_AT (*(gst_uchar *) addr));
+      SET_STACKTOP (CHAR_OOP_AT (*(gst_uchar *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_SHORT:
-      PUSH_INT (*(short *) addr);
+      SET_STACKTOP_INT (*(short *) addr);
       PRIM_SUCCEEDED;
 
     case CDATA_USHORT:
-      PUSH_INT (*(unsigned short *) addr);
+      SET_STACKTOP_INT (*(unsigned short *) addr);
       PRIM_SUCCEEDED;
 
     case CDATA_LONG:
-      PUSH_OOP (FROM_C_LONG (*(long *) addr));
+      SET_STACKTOP (FROM_C_LONG (*(long *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_ULONG:
-      PUSH_OOP (FROM_C_ULONG (*(unsigned long *) addr));
+      SET_STACKTOP (FROM_C_ULONG (*(unsigned long *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_FLOAT:
-      PUSH_OOP (floate_new (*(float *) addr));
+      SET_STACKTOP (floate_new (*(float *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_DOUBLE:
-      PUSH_OOP (floatd_new (*(double *) addr));
+      SET_STACKTOP (floatd_new (*(double *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_STRING:
@@ -4446,62 +4447,78 @@ primitive VMpr_CObject_at :
  strAddr = (char **) addr;
  if (*strAddr)
   {
-    PUSH_OOP (_gst_string_new (*strAddr));
+    SET_STACKTOP (_gst_string_new (*strAddr));
     PRIM_SUCCEEDED;
   }
  else
   {
-    PUSH_OOP (_gst_nil_oop);
+    SET_STACKTOP (_gst_nil_oop);
     PRIM_SUCCEEDED;
   }
       }
     case CDATA_OOP:
-      PUSH_OOP (*(OOP *) addr);
+      SET_STACKTOP (*(OOP *) addr);
       PRIM_SUCCEEDED;
 
     case CDATA_INT:
-      PUSH_OOP (FROM_C_INT (*(int *) addr));
+      SET_STACKTOP (FROM_C_INT (*(int *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_UINT:
-      PUSH_OOP (FROM_C_UINT (*(unsigned int *) addr));
+      SET_STACKTOP (FROM_C_UINT (*(unsigned int *) addr));
       PRIM_SUCCEEDED;
 
     case CDATA_LONG_DOUBLE:
-      PUSH_OOP (floatq_new (*(long double *) addr));
+      SET_STACKTOP (floatq_new (*(long double *) addr));
       PRIM_SUCCEEDED;
     }
-
  }
       else
  {
+  OOP baseOOP;
+  uintptr_t ofs;
+  inc_ptr incPtr;
+
   /* Non-integer oop3: use it as the type of the effective address. */
 
   if (id == prim_id (VMpr_CObject_derefAt))
     {
-      addr = *(char **) COBJECT_VALUE (oop1);
-      if (addr == 0)
+      if (!cobject_index_check (oop1, arg2, sizeof (uintptr_t)))
+ goto fail;
+
+      ofs = *(uintptr_t *) (((char *)cobject_value (oop1)) + arg2);
+      baseOOP = _gst_nil_oop;
+      if (ofs == 0)
  {
-  PUSH_OOP (_gst_nil_oop);
+  SET_STACKTOP (_gst_nil_oop);
   PRIM_SUCCEEDED;
  }
     }
           else
-    addr = COBJECT_VALUE (oop1);
-
-          addr += arg2; /* compute effective address */
+    {
+      /* No need to enforce bounds here (if we ever will, remember
+ that a pointer that is one-past the end of the object is
+ valid!).  */
+
+      gst_cobject cObj = (gst_cobject) OOP_TO_OBJ (oop1);
+      baseOOP = cObj->storage;
+      ofs = COBJECT_OFFSET_OBJ (cObj) + arg2;
+    }
 
-  /* oop3 could get GC'ed out of existence before it gets used:
-     it is not on the stack, and _gst_c_object_new could cause a GC */
-  inc_ptr incPtr;
+  /* oop3 could get GC'ed out of existence before it gets used: it is
+     not on the stack, and _gst_c_object_new_base could cause a GC */
   incPtr = INC_SAVE_POINTER ();
+  INC_ADD_OOP (baseOOP);
   INC_ADD_OOP (oop3);
-  PUSH_OOP (_gst_c_object_new (addr, oop3, _gst_c_object_class));
+  SET_STACKTOP (_gst_c_object_new_base (baseOOP, ofs, oop3,
+        _gst_c_object_class));
   INC_RESTORE_POINTER (incPtr);
   PRIM_SUCCEEDED;
  }
     }
-  UNPOP (3);
+
+ fail:
+  UNPOP (2);
   PRIM_FAILED;
 }
 
@@ -4522,13 +4539,12 @@ primitive VMpr_CObject_atPut [succeed,fail]
   if (IS_INT (oop2) && IS_INT (oop4))
     {
       char *addr;
-      intptr_t arg2;
-      intptr_t arg4;
-      arg2 = TO_INT (oop2);
-      addr = COBJECT_VALUE (oop1);
+      intptr_t arg2 = TO_INT (oop2);
+      intptr_t arg4 = TO_INT (oop4);
+      if (!cobject_index_check (oop1, arg2, _gst_c_type_size (arg4)))
+ goto fail;
 
-      addr += arg2; /* compute effective address */
-      arg4 = TO_INT (oop4);
+      addr = ((char *) cobject_value (oop1)) + arg2;
       switch (arg4)
  {
  case CDATA_CHAR: /* char */
@@ -4680,10 +4696,51 @@ primitive VMpr_CObject_atPut [succeed,fail]
  }
     }
 
+ fail:
   UNPOP (3);
   PRIM_FAILED;
 }
 
+/* CObject address */
+primitive VMpr_CObject_address [succeed]
+{
+  OOP oop1;
+  gst_cobject cObj;
+  uintptr_t ptr;
+  _gst_primitives_executed++;
+
+  oop1 = STACKTOP ();
+  cObj = (gst_cobject) OOP_TO_OBJ (oop1);
+  ptr = (uintptr_t) COBJECT_OFFSET_OBJ (cObj);
+
+  if (IS_NIL (cObj->storage))
+    SET_STACKTOP (FROM_C_ULONG (ptr));
+  else
+    SET_STACKTOP (FROM_C_LONG (ptr));
+}
+
+
+/* CObject address: */
+primitive VMpr_CObject_addressColon [succeed, fail]
+{
+  OOP oop1, oop2;
+  gst_cobject cObj;
+  _gst_primitives_executed++;
+
+  oop2 = POP_OOP ();
+  oop1 = STACKTOP ();
+  cObj = (gst_cobject) OOP_TO_OBJ (oop1);
+
+  if (IS_NIL (cObj->storage) ? IS_C_ULONG (oop2) : IS_C_LONG (oop2))
+    {
+      SET_COBJECT_OFFSET_OBJ (cObj, TO_C_LONG (oop2));
+      PRIM_SUCCEEDED;
+    }
+
+  UNPOP (1);
+  PRIM_FAILED;
+}
+
 /* CString replaceWith: aString */
 primitive VMpr_CString_replaceWith [succeed,fail]
 {
@@ -4706,7 +4763,7 @@ primitive VMpr_CString_replaceWith [succeed,fail]
       srcBase = STRING_OOP_CHARS (oop2);
       srcLen = NUM_INDEXABLE_FIELDS (oop2);
 
-      dstBase = *(gst_uchar **) COBJECT_VALUE (oop1);
+      dstBase = *(gst_uchar **) cobject_value (oop1);
       memcpy (dstBase, srcBase, srcLen);
       dstBase[srcLen] = '\0'; /* since it's a CString type, we NUL
    term it */
@@ -4731,7 +4788,7 @@ primitive VMpr_ByteArray_fromCData_size [succeed,fail]
     {
       intptr_t arg3 = TO_INT (oop3);
       OOP byteArrayOOP =
- _gst_byte_array_new (COBJECT_VALUE (oop2), arg3);
+ _gst_byte_array_new (cobject_value (oop2), arg3);
       SET_STACKTOP (byteArrayOOP);
       PRIM_SUCCEEDED;
     }
@@ -4754,7 +4811,7 @@ primitive VMpr_String_fromCData_size [succeed,fail]
     {
       intptr_t arg3 = TO_INT (oop3);
       OOP stringOOP =
- _gst_counted_string_new (COBJECT_VALUE (oop2), arg3);
+ _gst_counted_string_new (cobject_value (oop2), arg3);
       SET_STACKTOP (stringOOP);
       PRIM_SUCCEEDED;
     }
@@ -4772,7 +4829,7 @@ primitive VMpr_String_fromCData [succeed]
 
   oop2 = POP_OOP ();
   oop1 = STACKTOP ();
-  stringOOP = _gst_string_new (COBJECT_VALUE (oop2));
+  stringOOP = _gst_string_new (cobject_value (oop2));
   SET_STACKTOP (stringOOP);
   PRIM_SUCCEEDED;
 }
@@ -4795,7 +4852,7 @@ primitive VMpr_String_ByteArray_asCData :
       PTR data = xmalloc (size);
       if (data)
  {
-  OOP cObjectOOP = _gst_c_object_new (data, oop2, _gst_c_object_class);
+  OOP cObjectOOP = COBJECT_NEW (data, oop2, _gst_c_object_class);
   memcpy (data, OOP_TO_OBJ (oop1)->data, size);
   POP_OOP ();
   SET_STACKTOP (cObjectOOP);
diff --git a/packages/cairo/CairoContext.st b/packages/cairo/CairoContext.st
index 138d590..40ada84 100644
--- a/packages/cairo/CairoContext.st
+++ b/packages/cairo/CairoContext.st
@@ -772,11 +772,9 @@ CairoContextProvider subclass: CairoContext [
  in particular, affects the advance and not the extent."
  <category: 'text'>
  | ext |
- ext := CairoTextExtents new.
- ^[
-    Cairo textExtents: context utf8: aString extents: ext.
-    TextExtents from: ext
- ] ensure: [ ext free ]
+ ext := CairoTextExtents gcNew.
+ Cairo textExtents: context utf8: aString extents: ext.
+ ^TextExtents from: ext
     ]
 ].
 
diff --git a/packages/cairo/CairoTransform.st b/packages/cairo/CairoTransform.st
index 627fe92..2321ac9 100644
--- a/packages/cairo/CairoTransform.st
+++ b/packages/cairo/CairoTransform.st
@@ -41,11 +41,6 @@ CStruct subclass: CairoMatrix [
 
     <category: 'Cairo-C interface'>
 
-    CairoMatrix class >> new [
- <category: 'instance creation'>
- ^ super new addToBeFinalized
-    ]
-
     initIdentity [
  <category: 'initialize'>
         Cairo matrixInitIdentity: self.
@@ -53,20 +48,16 @@ CStruct subclass: CairoMatrix [
 
     withPoint: aPoint do: block [
  <category: 'using'>
-        | ox oy |
- ox := CDouble value: aPoint x.
- oy := CDouble value: aPoint y.
- ^ [
-    block value:self value:ox value: oy
- ] ensure: [
-    ox ifNotNil: [ :x | x free ].
-    oy ifNotNil: [ :y | y free ]].
+ ^block
+    value: self
+    value: (CDoubleType gcValue: aPoint x)
+    value: (CDoubleType gcValue: aPoint y)
     ]
 
     copy [
  <category: 'copying'>
         | shiny |
-        shiny := CairoMatrix new.
+        shiny := CairoMatrix gcNew.
         Cairo matrixInit: shiny
       xx: self xx value
       yx: self yx value
@@ -340,7 +331,7 @@ Transform subclass: MatrixTransform [
  "Initialize the receiver so that it represents the identity transform."
 
  <category: 'initialize'>
-        matrix := CairoMatrix new initIdentity.
+        matrix := CairoMatrix gcNew initIdentity.
     ]
 
     accept: aVisitor [
diff --git a/packages/sdl/libsdl/Display.st b/packages/sdl/libsdl/Display.st
index 8b0262d..6177fbb 100644
--- a/packages/sdl/libsdl/Display.st
+++ b/packages/sdl/libsdl/Display.st
@@ -245,13 +245,12 @@ as the destination for a Cairo surface.'>
 
  <category: 'drawing-SDL'>
  | r |
- r := SDL.SdlRect new.
- [r x value: aRect left.
+ r := SDL.SdlRect gcNew.
+ r x value: aRect left.
  r y value: aRect top.
  r w value: aRect width.
  r h value: aRect height.
- SdlVideo sdlFillRect: surface dstRect: r color: aColorNumber ]
-    ensure: [ r free ]
+ SdlVideo sdlFillRect: surface dstRect: r color: aColorNumber
     ]
 
     critical: aBlock [
diff --git a/tests/cobjects.ok b/tests/cobjects.ok
index 51a8578..ac3b504 100644
--- a/tests/cobjects.ok
+++ b/tests/cobjects.ok
@@ -97,3 +97,27 @@ returned value is StructB
 
 Execution begins...
 returned value is StructB
+
+Execution begins...
+8
+4369
+8738
+ByteArray (0 0 17 17 34 34 51 51 )
+ error: Invalid argument 8: offset out of range
+returned value is nil
+
+Execution begins...
+ error: Invalid argument -1: offset out of range
+returned value is nil
+
+Execution begins...
+ error: Invalid argument 7: offset out of range
+returned value is nil
+
+Execution begins...
+4369
+ error: Invalid argument 8: offset out of range
+returned value is nil
+
+Execution begins...
+returned value is true
diff --git a/tests/cobjects.st b/tests/cobjects.st
index 798be65..7435e6b 100644
--- a/tests/cobjects.st
+++ b/tests/cobjects.st
@@ -132,5 +132,47 @@ Eval [
     ^StructD new b elementType cObjectType
 ]
 
+
+"test some GCed CObjects."
+Eval [
+    cObject := (CShortType arrayType: 4) gcNew.
+    cObject storage size printNl.
+    cObject at: 1 put: 16r1111.
+    cObject at: 2 put: 16r2222.
+    cObject decr.
+    (cObject at: 2) printNl.
+    (cObject at: 3) printNl.
+    cObject at: 4 put: 16r3333.
+    cObject storage printNl.
+    cObject at: 5 put: 16rDEAD.
+]
+
+"test partly out of bound accesses"
+Eval [
+    cObject := (CShortType arrayType: 4) gcNew.
+    cObject adjPtrBy: 7.
+    cObject at: -4
+]
+
+Eval [
+    cObject := (CShortType arrayType: 4) gcNew.
+    cObject adjPtrBy: 7.
+    cObject at: 0
+]
+
+Eval [
+    cObject := (CShortType arrayType: 4) gcNew.
+    cIntObject := (cObject + 2) castTo: CIntType.
+    cIntObject value: 16r11111111.
+    (cObject at: 2) printNl.
+    cIntObject at: 1
+]
+
+Eval [
+    cObject := CCharType gcNew.
+    nil testCObjectPtr: cObject.
+    ^cObject isAbsolute "must be true"
+]
+
 " ### need a lot more!"
 

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk