package org.jruby.ext.jruby;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.ast.Node;
import org.jruby.ast.RootNode;
import org.jruby.ast.util.ArgsUtil;
import org.jruby.ir.IRBuilder;
import org.jruby.ir.IRManager;
import org.jruby.ir.IRScriptBody;
import org.jruby.ir.targets.JVMVisitor;
import org.jruby.ir.targets.JVMVisitorMethodContext;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.Library;
import org.jruby.util.ByteList;
import java.io.ByteArrayInputStream;
@JRubyModule(name="JRuby")
public class JRubyLibrary implements Library {
public void load(Ruby runtime, boolean wrap) {
runtime.getLoadService().loadFromClassLoader(runtime.getJRubyClassLoader(), "jruby/jruby.rb", false);
RubyModule JRuby = runtime.getOrCreateModule("JRuby");
JRuby.defineAnnotatedMethods(JRubyLibrary.class);
JRuby.defineAnnotatedMethods(JRubyUtilLibrary.class);
JRuby.defineClassUnder("ThreadLocal", runtime.getObject(), JRubyThreadLocal.ALLOCATOR)
.defineAnnotatedMethods(JRubyExecutionContextLocal.class);
JRuby.defineClassUnder("FiberLocal", runtime.getObject(), JRubyFiberLocal.ALLOCATOR)
.defineAnnotatedMethods(JRubyExecutionContextLocal.class);
RubyModule CONFIG = JRuby.defineModuleUnder("CONFIG");
CONFIG.getSingletonClass().defineAnnotatedMethods(JRubyConfig.class);
}
public static class JRubyConfig {
@JRubyMethod(name = "rubygems_disabled?")
public static IRubyObject rubygems_disabled_p(ThreadContext context, IRubyObject self) {
return RubyBoolean.newBoolean(context, context.runtime.getInstanceConfig().isDisableGems());
}
@JRubyMethod(name = "did_you_mean_disabled?")
public static IRubyObject did_you_mean_disabled_p(ThreadContext context, IRubyObject self) {
return RubyBoolean.newBoolean(context, context.runtime.getInstanceConfig().isDisableDidYouMean());
}
}
@JRubyMethod(module = true, name = {"reference", "ref"})
public static IRubyObject reference(ThreadContext context, IRubyObject recv, IRubyObject obj) {
return Java.getInstance(context.runtime, obj, false);
}
@JRubyMethod(module = true)
public static IRubyObject reference0(ThreadContext context, IRubyObject recv, IRubyObject obj) {
return Java.wrapJavaObject(context.runtime, obj);
}
@JRubyMethod(module = true)
public static IRubyObject runtime(ThreadContext context, IRubyObject recv) {
return Java.wrapJavaObject(context.runtime, context.runtime);
}
@JRubyMethod(module = true, name = {"dereference", "deref"})
public static IRubyObject dereference(ThreadContext context, IRubyObject recv, IRubyObject obj) {
Object unwrapped = JavaUtil.unwrapIfJavaObject(obj);
if (unwrapped == obj) {
throw context.runtime.newTypeError("got " + obj.inspect() + ", expected wrapped Java object");
}
if (!(unwrapped instanceof IRubyObject)) {
throw context.runtime.newTypeError("got " + obj.inspect() + ", expected Java-wrapped Ruby object");
}
return (IRubyObject) unwrapped;
}
@JRubyMethod(module = true)
public static IRubyObject with_current_runtime_as_global(ThreadContext context, IRubyObject recv, Block block) {
final Ruby current = context.runtime;
final Ruby global = Ruby.getGlobalRuntime();
try {
if (current != global) {
current.useAsGlobalRuntime();
}
return block.yield(context, runtime(context, recv));
}
finally {
if (Ruby.getGlobalRuntime() != global) {
global.useAsGlobalRuntime();
}
}
}
@JRubyMethod(module = true, rest = true, optional = 1)
public static IRubyObject set_context_class_loader(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
final ClassLoader loader;
if (args.length == 0 || args[0] == context.nil) {
loader = context.runtime.getJRubyClassLoader();
}
else {
loader = JavaUtil.unwrapJavaObject(args[0]);
}
java.lang.Thread.currentThread().setContextClassLoader(loader);
return Java.wrapJavaObject(context.runtime, loader);
}
@JRubyMethod(name = "security_restricted?", module = true)
public static RubyBoolean is_security_restricted(IRubyObject recv) {
final Ruby runtime = recv.getRuntime();
return RubyBoolean.newBoolean(runtime, Ruby.isSecurityRestricted());
}
@JRubyMethod(name = "security_restricted=", module = true)
public static IRubyObject set_security_restricted(IRubyObject recv, IRubyObject arg) {
Ruby.setSecurityRestricted(arg.isTrue());
return is_security_restricted(recv);
}
@JRubyMethod(module = true)
public static IRubyObject identity_hash(ThreadContext context, IRubyObject recv, IRubyObject obj) {
return context.runtime.newFixnum(System.identityHashCode(obj));
}
@JRubyMethod(module = true, name = "parse", alias = "ast_for", required = 1, optional = 3)
public static IRubyObject parse(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return Java.wrapJavaObject(context.runtime, parseImpl(context, args, block));
}
private static Node parseImpl(ThreadContext context, IRubyObject[] args, Block block) {
if (block.isGiven()) {
throw context.runtime.newNotImplementedError("JRuby.parse with block returning AST no longer supported");
}
final RubyString content = args[0].convertToString();
final String filename;
boolean extra_position_info = false; int lineno = 0;
switch (args.length) {
case 1 :
filename = "";
break;
case 2 :
filename = args[1].convertToString().toString();
break;
case 3 :
filename = args[1].convertToString().toString();
extra_position_info = args[2].isTrue();
break;
case 4 :
filename = args[1].convertToString().toString();
extra_position_info = args[2].isTrue();
lineno = args[3].convertToInteger().getIntValue();
break;
default :
throw new AssertionError("unexpected arguments: " + java.util.Arrays.toString(args));
}
final ByteList bytes = content.getByteList();
final DynamicScope scope = null;
final Node parseResult;
if (content.getEncoding() == ASCIIEncoding.INSTANCE) {
ByteArrayInputStream stream = new ByteArrayInputStream(bytes.getUnsafeBytes(), bytes.getBegin(), bytes.getRealSize());
parseResult = context.runtime.parseFile(stream, filename, scope, lineno);
}
else {
parseResult = context.runtime.parse(bytes, filename, scope, lineno, extra_position_info);
}
return parseResult;
}
@JRubyMethod(module = true, name = "compile_ir", required = 1, optional = 3)
public static IRubyObject compile_ir(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return Java.wrapJavaObject(context.runtime, compileIR(context, args, block));
}
private static IRScriptBody compileIR(ThreadContext context, IRubyObject[] args, Block block) {
RootNode node = (RootNode) parseImpl(context, args, block);
IRManager manager = new IRManager(context.runtime, context.runtime.getInstanceConfig());
manager.setDryRun(true);
IRScriptBody scope = (IRScriptBody) IRBuilder.buildRoot(manager, node).getScope();
scope.setScriptDynamicScope(node.getScope());
return scope;
}
@JRubyMethod(module = true, name = "compile", required = 1, optional = 3)
public static IRubyObject compile(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
final Ruby runtime = context.runtime;
final RubyString content = args[0].convertToString();
args[0] = content;
final RubyString filename = args.length > 1 ? args[1].convertToString() : RubyString.newEmptyString(runtime);
IRScriptBody scope = compileIR(context, args, block);
JVMVisitor visitor = new JVMVisitor(runtime);
JVMVisitorMethodContext methodContext = new JVMVisitorMethodContext();
byte[] bytes = visitor.compileToBytecode(scope, methodContext);
scope.getStaticScope().setModule( runtime.getTopSelf().getMetaClass() );
RubyClass CompiledScript = (RubyClass) runtime.getModule("JRuby").getConstantAt("CompiledScript");
return CompiledScript.newInstance(context, new IRubyObject[] {
filename,
runtime.newSymbol(scope.getId()),
content,
Java.getInstance(runtime, bytes)
}, Block.NULL_BLOCK);
}
@Deprecated
public static IRubyObject load_string_ext(ThreadContext context, IRubyObject recv) {
CoreExt.loadStringExtensions(context.runtime);
return context.nil;
}
@JRubyMethod(module = true)
public static IRubyObject subclasses(ThreadContext context, IRubyObject recv, IRubyObject arg) {
return subclasses(context, recv, arg instanceof RubyClass ? (RubyClass) arg : arg.getMetaClass(), false);
}
@JRubyMethod(module = true)
public static IRubyObject subclasses(ThreadContext context, IRubyObject recv, IRubyObject arg, IRubyObject opts) {
boolean recurseAll = false;
opts = ArgsUtil.getOptionsArg(context.runtime, opts);
if (opts != context.nil) {
IRubyObject all = ((RubyHash) opts).fastARef(context.runtime.newSymbol("all"));
if (all != null) recurseAll = all.isTrue();
}
return subclasses(context, recv, arg instanceof RubyClass ? (RubyClass) arg : arg.getMetaClass(), recurseAll);
}
private static RubyArray subclasses(ThreadContext context, final IRubyObject recv,
final RubyClass klass, final boolean recurseAll) {
final RubyArray subclasses = RubyArray.newArray(context.runtime);
RubyClass singletonClass = klass.getSingletonClass();
RubyObjectSpace.each_objectInternal(context, recv, new IRubyObject[] { singletonClass },
new Block(new JavaInternalBlockBody(context.runtime, Signature.ONE_ARGUMENT) {
@Override
public IRubyObject yield(ThreadContext context, IRubyObject[] args) {
return doYield(context, null, args[0]);
}
@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
if (klass != value) {
if (recurseAll) {
return subclasses.append(value);
}
if (((RubyClass) value).superclass(context) == klass) {
return subclasses.append(value);
}
}
return context.nil;
}
})
);
return subclasses;
}
}