jruby-safe 0.2.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +40 -0
- data/LICENSE +21 -0
- data/README.md +71 -0
- data/Rakefile +24 -0
- data/ext/java/sandbox/BoxedClass.java +35 -0
- data/ext/java/sandbox/SandboxFull.java +356 -0
- data/ext/java/sandbox/SandboxModule.java +39 -0
- data/ext/java/sandbox/SandboxProfile.java +22 -0
- data/ext/java/sandbox/SandboxService.java +17 -0
- data/jruby-safe.gemspec +26 -0
- data/lib/jruby-safe.rb +1 -0
- data/lib/sandbox.rb +18 -0
- data/lib/sandbox/binding.rb +27 -0
- data/lib/sandbox/prelude.rb +19 -0
- data/lib/sandbox/safe.rb +397 -0
- data/lib/sandbox/version.rb +3 -0
- data/spec/exploits_spec.rb +185 -0
- data/spec/sandbox_spec.rb +319 -0
- data/spec/support/foo.txt +1 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d4c4406a2d5008327fa781205106f933f84d15d
|
4
|
+
data.tar.gz: d09fc7ecc14e297a1c676cd0b61d15c87bef0bf5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 620de9cf546f5cd64d3e55a5c392be8598536ab9f2352da7d684344bd6bc1ccded760bba49f625e97fd0afec5dcd94acb02c9474763f65abe54e0e06982880df
|
7
|
+
data.tar.gz: c2c28c23359031787148a6d7dbf563f00843e94bc99640ba36baaa990ef2c93eb89f197b02ee622d02e9b5dc9c4e1eac53cc42733e50b33aca1e64e55aceed03
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.9
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in jruby_sandbox.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# this fork has some recent work done for 1.9, like File.foreach
|
7
|
+
gem 'fakefs', :git => 'git://github.com/codeschool/fakefs.git', :require => 'fakefs/safe', :ref => "6ae3212e3dab013b4b5e290d1aceba6466b30dec"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/codeschool/fakefs.git
|
3
|
+
revision: 6ae3212e3dab013b4b5e290d1aceba6466b30dec
|
4
|
+
ref: 6ae3212e3dab013b4b5e290d1aceba6466b30dec
|
5
|
+
specs:
|
6
|
+
fakefs (0.4.0)
|
7
|
+
|
8
|
+
PATH
|
9
|
+
remote: .
|
10
|
+
specs:
|
11
|
+
jruby-safe (0.2.2-java)
|
12
|
+
fakefs
|
13
|
+
|
14
|
+
GEM
|
15
|
+
remote: http://rubygems.org/
|
16
|
+
specs:
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
rake (0.9.2)
|
19
|
+
rake-compiler (0.7.9)
|
20
|
+
rake
|
21
|
+
rspec (2.6.0)
|
22
|
+
rspec-core (~> 2.6.0)
|
23
|
+
rspec-expectations (~> 2.6.0)
|
24
|
+
rspec-mocks (~> 2.6.0)
|
25
|
+
rspec-core (2.6.4)
|
26
|
+
rspec-expectations (2.6.0)
|
27
|
+
diff-lcs (~> 1.1.2)
|
28
|
+
rspec-mocks (2.6.0)
|
29
|
+
yard (0.7.2)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
java
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
fakefs!
|
36
|
+
jruby-safe!
|
37
|
+
rake
|
38
|
+
rake-compiler
|
39
|
+
rspec
|
40
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2006 Ola Bini <ola@ologix.com>
|
2
|
+
Copyright (c) 2011 Dray Lacy and Eric Allam
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
JRuby Safe
|
2
|
+
=============
|
3
|
+
|
4
|
+
The JRuby safe is a clone of [jruby-sandbox](https://github.com/omghax/jruby-sandbox)
|
5
|
+
|
6
|
+
## Building
|
7
|
+
|
8
|
+
To build the JRuby extension, run `rake compile`. This will build the
|
9
|
+
`lib/sandbox/sandbox.jar` file, which `lib/sandbox.rb` loads.
|
10
|
+
|
11
|
+
## Testing
|
12
|
+
|
13
|
+
$ bundle install --path vendor/bundle
|
14
|
+
$ bundle exec rake spec
|
15
|
+
|
16
|
+
## Basic Usage
|
17
|
+
|
18
|
+
Sandbox gives you a self-contained JRuby interpreter in which to eval
|
19
|
+
code without polluting the host environment.
|
20
|
+
|
21
|
+
>> require "sandbox"
|
22
|
+
=> true
|
23
|
+
>> sand = Sandbox::Full.new
|
24
|
+
=> #<Sandbox::Full:0x46377e2a>
|
25
|
+
>> sand.eval("x = 1 + 2") # we've defined x in the sandbox
|
26
|
+
=> 3
|
27
|
+
>> sand.eval("x")
|
28
|
+
=> 3
|
29
|
+
>> x # but it hasn't leaked out into the host interpreter
|
30
|
+
NameError: undefined local variable or method `x' for #<Object:0x11cdc190>
|
31
|
+
|
32
|
+
There's also `Sandbox::Full#require`, which lets you invoke `Kernel#require`
|
33
|
+
directly for the sandbox, so you can load any trusted core libraries. Note that
|
34
|
+
this is a direct binding to `Kernel#require`, so it will only load ruby stdlib
|
35
|
+
libraries (i.e. no rubygems support yet).
|
36
|
+
|
37
|
+
## Sandbox::Safe usage
|
38
|
+
|
39
|
+
Sandbox::Safe exposes an `#activate!` method which will lock down the sandbox,
|
40
|
+
removing unsafe methods. Before calling `#activate!`, Sandbox::Safe is the same
|
41
|
+
as Sandbox::Full.
|
42
|
+
|
43
|
+
>> require 'sandbox'
|
44
|
+
=> true
|
45
|
+
>> sand = Sandbox.safe
|
46
|
+
=> #<Sandbox::Safe:0x17072b90>
|
47
|
+
>> sand.eval %{`echo HELLO`}
|
48
|
+
=> "HELLO\n"
|
49
|
+
>> sand.activate!
|
50
|
+
>> sand.eval %{`echo HELLO`}
|
51
|
+
Sandbox::SandboxException: NoMethodError: undefined method ``' for main:Object
|
52
|
+
|
53
|
+
Sandbox::Safe works by whitelisting methods to keep, and removing the rest.
|
54
|
+
Checkout sandbox.rb for which methods are kept.
|
55
|
+
|
56
|
+
Sandbox::Safe.activate! will also isolate the sandbox environment from the
|
57
|
+
filesystem using FakeFS.
|
58
|
+
|
59
|
+
>> require 'sandbox'
|
60
|
+
=> true
|
61
|
+
>> s = Sandbox.safe
|
62
|
+
=> #<Sandbox::Safe:0x3fdb8a73>
|
63
|
+
>> s.eval('Dir["/"]')
|
64
|
+
=> ["/"]
|
65
|
+
>> s.eval('Dir["/*"]')
|
66
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
67
|
+
> s.activate!
|
68
|
+
>> s.eval('Dir["/*"]')
|
69
|
+
=> []
|
70
|
+
> Dir['/*']
|
71
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake/javaextensiontask'
|
5
|
+
|
6
|
+
Rake::JavaExtensionTask.new('sandbox') do |ext|
|
7
|
+
jruby_home = RbConfig::CONFIG['prefix']
|
8
|
+
ext.ext_dir = 'ext/java'
|
9
|
+
ext.lib_dir = 'lib/sandbox'
|
10
|
+
jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar']
|
11
|
+
ext.classpath = jars.map { |x| File.expand_path(x) }.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
17
|
+
t.pattern = 'spec/**/*_spec.rb'
|
18
|
+
t.rspec_opts = ['--backtrace']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Make sure the jar is up to date before running specs
|
22
|
+
task :spec => :compile
|
23
|
+
|
24
|
+
task :default => :spec
|
@@ -0,0 +1,35 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyClass;
|
5
|
+
import org.jruby.anno.JRubyClass;
|
6
|
+
import org.jruby.anno.JRubyMethod;
|
7
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
8
|
+
import org.jruby.runtime.Block;
|
9
|
+
|
10
|
+
@JRubyClass(name="BoxedClass")
|
11
|
+
public class BoxedClass {
|
12
|
+
protected static RubyClass createBoxedClassClass(final Ruby runtime) {
|
13
|
+
RubyClass cObject = runtime.getObject();
|
14
|
+
RubyClass cBoxedClass = runtime.defineClass("BoxedClass", cObject, cObject.getAllocator());
|
15
|
+
cBoxedClass.defineAnnotatedMethods(BoxedClass.class);
|
16
|
+
|
17
|
+
return cBoxedClass;
|
18
|
+
}
|
19
|
+
|
20
|
+
@JRubyMethod(module=true, rest=true)
|
21
|
+
public static IRubyObject method_missing(IRubyObject recv, IRubyObject[] args, Block block) {
|
22
|
+
IRubyObject[] args2 = new IRubyObject[args.length - 1];
|
23
|
+
System.arraycopy(args, 1, args2, 0, args2.length);
|
24
|
+
String name = args[0].toString();
|
25
|
+
|
26
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
27
|
+
return box.runMethod(recv, name, args2, block);
|
28
|
+
}
|
29
|
+
|
30
|
+
@JRubyMethod(name="new", meta=true, rest=true)
|
31
|
+
public static IRubyObject _new(IRubyObject recv, IRubyObject[] args, Block block) {
|
32
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
33
|
+
return box.runMethod(recv, "new", args, block);
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,356 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import java.util.Collection;
|
4
|
+
import java.util.Map;
|
5
|
+
|
6
|
+
import org.jruby.Ruby;
|
7
|
+
import org.jruby.RubyClass;
|
8
|
+
import org.jruby.RubyBinding;
|
9
|
+
import org.jruby.RubyInstanceConfig;
|
10
|
+
import org.jruby.RubyKernel;
|
11
|
+
import org.jruby.RubyModule;
|
12
|
+
import org.jruby.RubyObject;
|
13
|
+
import org.jruby.CompatVersion;
|
14
|
+
import org.jruby.anno.JRubyClass;
|
15
|
+
import org.jruby.anno.JRubyMethod;
|
16
|
+
import org.jruby.internal.runtime.methods.DynamicMethod;
|
17
|
+
import org.jruby.runtime.Block;
|
18
|
+
import org.jruby.runtime.Binding;
|
19
|
+
import org.jruby.runtime.ObjectAllocator;
|
20
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
21
|
+
import org.jruby.common.IRubyWarnings;
|
22
|
+
import org.jruby.exceptions.RaiseException;
|
23
|
+
import org.jruby.runtime.DynamicScope;
|
24
|
+
|
25
|
+
|
26
|
+
@JRubyClass(name="Sandbox::Full")
|
27
|
+
public class SandboxFull extends RubyObject {
|
28
|
+
protected static ObjectAllocator FULL_ALLOCATOR = new ObjectAllocator() {
|
29
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
30
|
+
return new SandboxFull(runtime, klass);
|
31
|
+
}
|
32
|
+
};
|
33
|
+
|
34
|
+
private Ruby wrapped;
|
35
|
+
private DynamicScope currentScope;
|
36
|
+
|
37
|
+
protected SandboxFull(Ruby runtime, RubyClass type) {
|
38
|
+
super(runtime, type);
|
39
|
+
reload();
|
40
|
+
}
|
41
|
+
|
42
|
+
@JRubyMethod
|
43
|
+
public IRubyObject reload() {
|
44
|
+
RubyInstanceConfig cfg = new RubyInstanceConfig();
|
45
|
+
cfg.setObjectSpaceEnabled(getRuntime().getInstanceConfig().isObjectSpaceEnabled());
|
46
|
+
cfg.setInput(getRuntime().getInstanceConfig().getInput());
|
47
|
+
cfg.setOutput(getRuntime().getInstanceConfig().getOutput());
|
48
|
+
cfg.setError(getRuntime().getInstanceConfig().getError());
|
49
|
+
cfg.setCompatVersion(CompatVersion.RUBY1_9);
|
50
|
+
cfg.setScriptFileName("(sandbox)");
|
51
|
+
|
52
|
+
SandboxProfile profile = new SandboxProfile(this);
|
53
|
+
cfg.setProfile(profile);
|
54
|
+
|
55
|
+
wrapped = Ruby.newInstance(cfg);
|
56
|
+
currentScope = wrapped.getCurrentContext().getCurrentScope();
|
57
|
+
|
58
|
+
BoxedClass.createBoxedClassClass(wrapped);
|
59
|
+
|
60
|
+
return this;
|
61
|
+
}
|
62
|
+
|
63
|
+
private static abstract class EvalBinding {
|
64
|
+
public abstract Binding convertToBinding(IRubyObject scope);
|
65
|
+
}
|
66
|
+
|
67
|
+
private static EvalBinding evalBinding19 = new EvalBinding() {
|
68
|
+
@Override
|
69
|
+
public Binding convertToBinding(IRubyObject scope) {
|
70
|
+
if (scope instanceof RubyBinding) {
|
71
|
+
return ((RubyBinding)scope).getBinding().cloneForEval();
|
72
|
+
} else {
|
73
|
+
throw scope.getRuntime().newTypeError("wrong argument type " + scope.getMetaClass() + " (expected Binding)");
|
74
|
+
}
|
75
|
+
}
|
76
|
+
};
|
77
|
+
|
78
|
+
@JRubyMethod(required=1)
|
79
|
+
public IRubyObject eval(IRubyObject str) {
|
80
|
+
try {
|
81
|
+
IRubyObject result = wrapped.evalScriptlet(str.asJavaString(), currentScope);
|
82
|
+
return unbox(result);
|
83
|
+
} catch (RaiseException e) {
|
84
|
+
String msg = e.getException().callMethod(wrapped.getCurrentContext(), "message").asJavaString();
|
85
|
+
String path = e.getException().type().getName();
|
86
|
+
RubyClass eSandboxException = (RubyClass) getRuntime().getClassFromPath("Sandbox::SandboxException");
|
87
|
+
throw new RaiseException(getRuntime(), eSandboxException, path + ": " + msg, false);
|
88
|
+
} catch (Exception e) {
|
89
|
+
e.printStackTrace();
|
90
|
+
getRuntime().getWarnings().warn(IRubyWarnings.ID.MISCELLANEOUS, "NativeException: " + e);
|
91
|
+
return getRuntime().getNil();
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
@JRubyMethod(required=2)
|
96
|
+
public IRubyObject eval_with_binding(IRubyObject str, IRubyObject argBinding) {
|
97
|
+
try {
|
98
|
+
//IRubyObject result = wrapped.evalScriptlet(str.asJavaString());
|
99
|
+
//boolean bindingGiven = args.length > 1 && !args[1].isNil();
|
100
|
+
//Binding binding = bindingGiven ? evalBinding.convertToBinding(args[1]) : context.currentBinding();
|
101
|
+
Binding binding = evalBinding19.convertToBinding(argBinding);
|
102
|
+
DynamicScope scope = binding.getEvalScope(getRuntime());
|
103
|
+
IRubyObject result = wrapped.evalScriptlet(str.asJavaString(), scope);
|
104
|
+
return unbox(result);
|
105
|
+
} catch (RaiseException e) {
|
106
|
+
String msg = e.getException().callMethod(wrapped.getCurrentContext(), "message").asJavaString();
|
107
|
+
String path = e.getException().type().getName();
|
108
|
+
RubyClass eSandboxException = (RubyClass) getRuntime().getClassFromPath("Sandbox::SandboxException");
|
109
|
+
throw new RaiseException(getRuntime(), eSandboxException, path + ": " + msg, false);
|
110
|
+
} catch (Exception e) {
|
111
|
+
e.printStackTrace();
|
112
|
+
getRuntime().getWarnings().warn(IRubyWarnings.ID.MISCELLANEOUS, "NativeException: " + e);
|
113
|
+
return getRuntime().getNil();
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
@JRubyMethod(name="import", required=1)
|
118
|
+
public IRubyObject _import(IRubyObject klass) {
|
119
|
+
if (!(klass instanceof RubyModule)) {
|
120
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
121
|
+
}
|
122
|
+
String name = ((RubyModule) klass).getName();
|
123
|
+
importClassPath(name, false);
|
124
|
+
return getRuntime().getNil();
|
125
|
+
}
|
126
|
+
|
127
|
+
@JRubyMethod(required=1)
|
128
|
+
public IRubyObject ref(IRubyObject klass) {
|
129
|
+
if (!(klass instanceof RubyModule)) {
|
130
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
131
|
+
}
|
132
|
+
String name = ((RubyModule) klass).getName();
|
133
|
+
importClassPath(name, true);
|
134
|
+
return getRuntime().getNil();
|
135
|
+
}
|
136
|
+
|
137
|
+
private RubyModule importClassPath(String path, final boolean link) {
|
138
|
+
RubyModule runtimeModule = getRuntime().getObject();
|
139
|
+
RubyModule wrappedModule = wrapped.getObject();
|
140
|
+
|
141
|
+
if (path.startsWith("#")) {
|
142
|
+
throw getRuntime().newArgumentError("can't import anonymous class " + path);
|
143
|
+
}
|
144
|
+
|
145
|
+
for (String name : path.split("::")) {
|
146
|
+
runtimeModule = (RubyModule) runtimeModule.getConstantAt(name);
|
147
|
+
// Create the module when it did not exist yet...
|
148
|
+
if (wrappedModule.const_defined_p(wrapped.getCurrentContext(), wrapped.newString(name)).isFalse()) {
|
149
|
+
// The BoxedClass takes the place of Object as top of the inheritance
|
150
|
+
// hierarchy. As a result, we can intercept all new instances that are
|
151
|
+
// created and all method_missing calls.
|
152
|
+
RubyModule sup = wrapped.getClass("BoxedClass");
|
153
|
+
if (!link && runtimeModule instanceof RubyClass) {
|
154
|
+
// If we're importing a class, recursively import all of its
|
155
|
+
// superclasses as well.
|
156
|
+
sup = importClassPath(runtimeModule.getSuperClass().getName(), true);
|
157
|
+
}
|
158
|
+
|
159
|
+
RubyClass klass = (RubyClass) sup;
|
160
|
+
if (wrappedModule == wrapped.getObject()) {
|
161
|
+
|
162
|
+
if (link || runtimeModule instanceof RubyClass) { // if this is a ref and not an import
|
163
|
+
wrappedModule = wrapped.defineClass(name, klass, klass.getAllocator());
|
164
|
+
} else {
|
165
|
+
wrappedModule = wrapped.defineModule(name);
|
166
|
+
}
|
167
|
+
|
168
|
+
} else {
|
169
|
+
if (runtimeModule instanceof RubyClass) {
|
170
|
+
wrappedModule = wrappedModule.defineClassUnder(name, klass, klass.getAllocator());
|
171
|
+
} else {
|
172
|
+
wrappedModule = wrappedModule.defineModuleUnder(name);
|
173
|
+
}
|
174
|
+
|
175
|
+
}
|
176
|
+
} else {
|
177
|
+
// ...or just resolve it, if it was already known
|
178
|
+
wrappedModule = (RubyModule) wrappedModule.getConstantAt(name);
|
179
|
+
}
|
180
|
+
|
181
|
+
// Check the consistency of the hierarchy
|
182
|
+
if (runtimeModule instanceof RubyClass) {
|
183
|
+
if (!link && !runtimeModule.getSuperClass().getName().equals(wrappedModule.getSuperClass().getName())) {
|
184
|
+
throw getRuntime().newTypeError("superclass mismatch for class " + runtimeModule.getSuperClass().getName());
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
if (link || runtimeModule instanceof RubyClass) {
|
189
|
+
linkObject(runtimeModule, wrappedModule);
|
190
|
+
} else {
|
191
|
+
copyMethods(runtimeModule, wrappedModule);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
return runtimeModule;
|
196
|
+
}
|
197
|
+
|
198
|
+
private void copyMethods(RubyModule from, RubyModule to) {
|
199
|
+
to.getMethodsForWrite().putAll(from.getMethods());
|
200
|
+
to.getSingletonClass().getMethodsForWrite().putAll(from.getSingletonClass().getMethods());
|
201
|
+
}
|
202
|
+
|
203
|
+
@JRubyMethod(required=2)
|
204
|
+
public IRubyObject keep_methods(IRubyObject className, IRubyObject methods) {
|
205
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
206
|
+
if (module != null) {
|
207
|
+
keepMethods(module, methods.convertToArray());
|
208
|
+
}
|
209
|
+
return methods;
|
210
|
+
}
|
211
|
+
|
212
|
+
@JRubyMethod(required=2)
|
213
|
+
public IRubyObject keep_singleton_methods(IRubyObject className, IRubyObject methods) {
|
214
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
215
|
+
if (module != null) {
|
216
|
+
keepMethods(module, methods.convertToArray());
|
217
|
+
}
|
218
|
+
return methods;
|
219
|
+
}
|
220
|
+
|
221
|
+
private void keepMethods(RubyModule module, Collection retain) {
|
222
|
+
for (Map.Entry<String, DynamicMethod> methodEntry : module.getMethods().entrySet()) {
|
223
|
+
String methodName = methodEntry.getKey();
|
224
|
+
if (!retain.contains(methodName)) {
|
225
|
+
removeMethod(module, methodName);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
@JRubyMethod(required=2)
|
231
|
+
public IRubyObject remove_method(IRubyObject className, IRubyObject methodName) {
|
232
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
233
|
+
if (module != null) {
|
234
|
+
removeMethod(module, methodName.asJavaString());
|
235
|
+
}
|
236
|
+
return getRuntime().getNil();
|
237
|
+
}
|
238
|
+
|
239
|
+
@JRubyMethod(required=2)
|
240
|
+
public IRubyObject remove_singleton_method(IRubyObject className, IRubyObject methodName) {
|
241
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
242
|
+
if (module != null) {
|
243
|
+
removeMethod(module, methodName.asJavaString());
|
244
|
+
}
|
245
|
+
return getRuntime().getNil();
|
246
|
+
}
|
247
|
+
|
248
|
+
private void removeMethod(RubyModule module, String methodName) {
|
249
|
+
// System.err.println("removing method " + methodName + " from " + module.inspect().asJavaString());
|
250
|
+
module.removeMethod(wrapped.getCurrentContext(), methodName);
|
251
|
+
}
|
252
|
+
|
253
|
+
@JRubyMethod(required=1)
|
254
|
+
public IRubyObject load(IRubyObject str) {
|
255
|
+
try {
|
256
|
+
wrapped.getLoadService().load(str.asJavaString(), true);
|
257
|
+
return getRuntime().getTrue();
|
258
|
+
} catch (RaiseException e) {
|
259
|
+
e.printStackTrace();
|
260
|
+
return getRuntime().getFalse();
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
@JRubyMethod(required=1)
|
265
|
+
public IRubyObject require(IRubyObject str) {
|
266
|
+
try {
|
267
|
+
IRubyObject result = RubyKernel.require(wrapped.getKernel(), wrapped.newString(str.asJavaString()), Block.NULL_BLOCK);
|
268
|
+
return unbox(result);
|
269
|
+
} catch (RaiseException e) {
|
270
|
+
e.printStackTrace();
|
271
|
+
return getRuntime().getFalse();
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
275
|
+
private IRubyObject unbox(IRubyObject obj) {
|
276
|
+
return box(obj);
|
277
|
+
}
|
278
|
+
|
279
|
+
private IRubyObject rebox(IRubyObject obj) {
|
280
|
+
return box(obj);
|
281
|
+
}
|
282
|
+
|
283
|
+
private IRubyObject box(IRubyObject obj) {
|
284
|
+
if (obj.isImmediate()) {
|
285
|
+
return cross(obj);
|
286
|
+
} else {
|
287
|
+
// If this object already existed and was returned from the wrapped
|
288
|
+
// runtime on an earlier occasion, it will already contain a link to its
|
289
|
+
// brother in the regular runtime and we can safely return that link.
|
290
|
+
IRubyObject link = getLinkedObject(obj);
|
291
|
+
if (!link.isNil()) {
|
292
|
+
IRubyObject box = getLinkedBox(obj);
|
293
|
+
if (box == this) return link;
|
294
|
+
}
|
295
|
+
|
296
|
+
// Is the class already known on both sides of the fence?
|
297
|
+
IRubyObject klass = constFind(obj.getMetaClass().getRealClass().getName());
|
298
|
+
link = getRuntime().getNil();
|
299
|
+
if (!klass.isNil()) {
|
300
|
+
link = getLinkedObject(klass);
|
301
|
+
}
|
302
|
+
|
303
|
+
if (link.isNil()) {
|
304
|
+
return cross(obj);
|
305
|
+
} else {
|
306
|
+
IRubyObject v = ((RubyClass)klass).allocate();
|
307
|
+
linkObject(obj, v);
|
308
|
+
return v;
|
309
|
+
}
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
private IRubyObject cross(IRubyObject obj) {
|
314
|
+
IRubyObject dumped = wrapped.getModule("Marshal").callMethod(wrapped.getCurrentContext(), "dump", obj);
|
315
|
+
return getRuntime().getModule("Marshal").callMethod(getRuntime().getCurrentContext(), "load", dumped);
|
316
|
+
}
|
317
|
+
|
318
|
+
protected static IRubyObject getLinkedObject(IRubyObject arg) {
|
319
|
+
IRubyObject object = arg.getRuntime().getNil();
|
320
|
+
if (arg.getInstanceVariables().hasInstanceVariable("__link__")) {
|
321
|
+
object = (IRubyObject) arg.getInstanceVariables().getInstanceVariable("__link__");
|
322
|
+
}
|
323
|
+
return object;
|
324
|
+
}
|
325
|
+
|
326
|
+
protected static IRubyObject getLinkedBox(IRubyObject arg) {
|
327
|
+
IRubyObject object = arg.getRuntime().getNil();
|
328
|
+
if (arg.getInstanceVariables().hasInstanceVariable("__box__")) {
|
329
|
+
object = (IRubyObject) arg.getInstanceVariables().getInstanceVariable("__box__");
|
330
|
+
}
|
331
|
+
return object;
|
332
|
+
}
|
333
|
+
|
334
|
+
private void linkObject(IRubyObject runtimeObject, IRubyObject wrappedObject) {
|
335
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__link__", runtimeObject);
|
336
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__box__", this);
|
337
|
+
}
|
338
|
+
|
339
|
+
private IRubyObject constFind(String path) {
|
340
|
+
try {
|
341
|
+
return wrapped.getClassFromPath(path);
|
342
|
+
} catch (Exception e) {
|
343
|
+
return wrapped.getNil();
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
protected IRubyObject runMethod(IRubyObject recv, String name, IRubyObject[] args, Block block) {
|
348
|
+
IRubyObject[] args2 = new IRubyObject[args.length];
|
349
|
+
for (int i = 0; i < args.length; i++) {
|
350
|
+
args2[i] = unbox(args[i]);
|
351
|
+
}
|
352
|
+
IRubyObject recv2 = unbox(recv);
|
353
|
+
IRubyObject result = recv2.callMethod(getRuntime().getCurrentContext(), name, args2, block);
|
354
|
+
return rebox(result);
|
355
|
+
}
|
356
|
+
}
|