Skip to content


Subversion checkout URL

You can clone with
Download ZIP


NoMethodError returns Smalltalk class name for RC Classes #96

merged 8 commits into from

3 participants


NoMethodError: NoMethodError: undefined method `empty' for RcKeyValueDictionary

it should read

NoMethodError: NoMethodError: undefined method `empty' for RCHash


Fixing this isn't possible when using __resolve_smalltalk_global to assign the class name. This is exactly the same as doing:

   >> SS = String
   => NoMethodError: undefined method `foo' for String:Class

The name will still be from the original class. __resolve_smalltalk_global wasn't really meant to be used outside bootstrap, either. I have added expose_smalltalk_global_as which takes two Strings, a Smalltalk class name and a Ruby name. It will install the constant under the Ruby name in the current namespace (so you don't have to do the assignment), and it'll setup the Ruby namespace so it prints the desired name for the class.


Awesome, thanks!


I've added a test for this behaviour.
@pbm, @monty, if you don't object to this additional method, I'll just merge it. It's new API, anyway, so it wouldn't break existing code.

pbm commented

I think the test should cover both the transient and persistent cases. So we need to test that a transient use of expose_smalltalk_global, followed by a commit does not persist the symbol. And that a persistent use of expose_smalltalk_global, followed by a commit does persist the symbol.


The problem with this implementation is now, that once you persistently expose a smalltalk class, you cannot change its name anymore (or it is very hard). I've played with different ways to fix this, but couldn't come up with a good solution. Is this ok? /cc @jc00ke @pbm


Looks good for now.



@timfel timfel merged commit 392c709 into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
6 src/kernel/bootstrap/Kernel.rb
@@ -1,10 +1,16 @@
module Kernel
+ # This is for bootstrap code only
primitive_nobridge '__resolve_smalltalk_global', 'resolveSmalltalkGlobal:'
# _smalltalk_global_put for use by bootstrap code only
primitive_nobridge '__smalltalk_global_put', 'smalltalkUserGlobalsAt:put:'
+ # Use this method to expose a Smalltalk class to Ruby land under a
+ # specific name. It will install the class within the current
+ # namespace under the specified name
+ primitive 'expose_smalltalk_global_as', 'exposeSmalltalkGlobal:as:'
# Loads and executes the Ruby program in the file +name+. If the
# filename does not resolve to an absolute path, the file is searched for
# in the library directories listed in <tt>$:</tt>. If the optional
24 src/smalltalk/ruby/mcz/
@@ -38,6 +38,30 @@ resolveSmalltalkGlobal: aName
set class Kernel
category: '*maglev-runtime'
+exposeSmalltalkGlobal: aName as: aRubyName
+ | assoc cld ns tns sym |
+ assoc := System myUserProfile resolveSymbol: (aName asSymbol) .
+ assoc ifNil:[ self error:'Smalltalk global ' , aName , ' not found' ].
+ cld := GsProcess _current _clientData.
+ ns := cld
+ ifNotNil: [(cld at: 5) last theNonMetaClass "_rubyThreadDataAt:5 -> rtModuleStack"]
+ ifNil: [Object].
+ ns rubyConstAt: (sym := aRubyName asSymbol) env: 1 put: assoc _value.
+ (tns := assoc _value transientNameSpaceForStore: 1) parent ifNil: [
+ assoc _value changeNameTo: sym.
+ tns _name: sym;parent: (ns transientNameSpaceForStore: 1)].
+ ^ assoc _value
+set class Kernel
+category: '*maglev-runtime'
"a ruby primitive"
| ns arr |
2  src/smalltalk/ruby/mcz/
@@ -249,9 +249,9 @@ method:
rubyFullName: envId
"called from Smalltalk code only"
| ns nam |
- self isMeta ifTrue:[ ^ '' copy ].
(ns := self nameSpace: envId) ifNotNil:[ ^ ns fullName ].
((nam := name) isNil or: [nam size == 0]) ifTrue: [
+ self isMeta ifTrue: [ ^ (destClass rubyFullName: envId), ':Class' ].
^ '#<Class:0x', self asOop hex, '>' ].
^ String withAll: nam
40 src/test/gh96.rb
@@ -0,0 +1,40 @@
+def assert_constants
+ raise "TestCase wasn't exposed properly" unless defined? Gh96::StTestCase
+ raise "TestCase wasn't exposed properly" unless defined? Gh96::Gh96::StTestCase
+ raise "TestCase wasn't exposed properly" if defined? StTestCase
+ raise "StTestCase doesn't report its Ruby name" unless == "Gh96::StTestCase"
+ raise "StTestCase doesn't report its first Ruby name" unless == "Gh96::StTestCase"
+ raise "StTestCase exposure doesn't expose the same class" unless Gh96::Gh96::StTestCase == Gh96::StTestCase
+def remove_constants
+ Gh96::Gh96.remove_const :StTestCase
+ Gh96.remove_const :StTestCase
+ Gh96.remove_const :Gh96
+ Object.remove_const :Gh96
+Maglev.persistent do
+ module Gh96
+ expose_smalltalk_global_as("TestCase", "StTestCase")
+ class Gh96
+ expose_smalltalk_global_as("TestCase", "StTestCase")
+ end
+ end
+ assert_constants
+ Maglev.commit_transaction
+ raise "Persistently exposed global wasn't committed to the stone, but should!" unless system("maglev-ruby -e 'Gh96::StTestCase' >/dev/null 2>/dev/null")
+ remove_constants
+module Gh96
+ expose_smalltalk_global_as("TestCase", "StTestCase")
+ class Gh96
+ expose_smalltalk_global_as("TestCase", "StTestCase")
+ end
+raise "Transiently exposed global was committed to the stone, but shouldn't!" if system("maglev-ruby -e 'Gh96::StTestCase' >/dev/null 2>/dev/null")
Something went wrong with that request. Please try again.