Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9e42dce72116
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4d6c302fa051
Choose a head ref
  • 4 commits
  • 4 files changed
  • 1 contributor

Commits on Aug 24, 2017

  1. Copy the full SHA
    65b88ce View commit details
  2. Copy the full SHA
    23eeafa View commit details
  3. move JRuby's compile/parse implementation into native

    ... some of it is broken (for a long-time) -> getting AST from block
    kares committed Aug 24, 2017
    Copy the full SHA
    bce51f5 View commit details
  4. Copy the full SHA
    4d6c302 View commit details
120 changes: 118 additions & 2 deletions core/src/main/java/org/jruby/ext/jruby/JRubyLibrary.java
Original file line number Diff line number Diff line change
@@ -30,19 +30,30 @@
***** END LICENSE BLOCK *****/
package org.jruby.ext.jruby;

import org.jcodings.specific.ASCIIEncoding;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.ast.Node;
import org.jruby.ast.RootNode;
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.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.Library;
import org.jruby.util.ByteList;

import java.io.ByteArrayInputStream;

/**
* Native part of require 'jruby'. Provides methods for swapping between the
@@ -104,6 +115,18 @@ public static IRubyObject runtime(ThreadContext context, IRubyObject recv) {
return Java.wrapJavaObject(context.runtime, context.runtime); // context.nil.getRuntime()
}

/**
* JRuby.config a shortcut for JRuby.runtime.instance_config
* @param context
* @param recv
* @return a wrapped RubyInstanceConfig
* @since 9.2
*/
@JRubyMethod(module = true)
public static IRubyObject config(ThreadContext context, IRubyObject recv) {
return Java.wrapJavaObject(context.runtime, context.runtime.getInstanceConfig());
}

/**
* Unwrap the given Java-integration-wrapped object, returning the unwrapped
* object. If the wrapped object is not a Ruby object, an error will raise.
@@ -166,4 +189,97 @@ public static IRubyObject identity_hash(ThreadContext context, IRubyObject recv,
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) {
// def parse(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, lineno = 0, &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) {
// binary content, parse as though from a stream
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) {
// def compile_ir(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, &block)
return Java.wrapJavaObject(context.runtime, compileIR(context, args, block));
}

private static IRScriptBody compileIR(ThreadContext context, IRubyObject[] args, Block block) {
RootNode result = (RootNode) parseImpl(context, args, block);
IRManager manager = new IRManager(context.runtime.getInstanceConfig());
IRScriptBody scope = (IRScriptBody) IRBuilder.buildRoot(manager, result).getScope();
scope.setTopLevelBindingScope(result.getScope());
return scope;
}

@JRubyMethod(module = true, name = "compile", required = 1, optional = 3)
public static IRubyObject compile(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
// def compile(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, &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");
// JRuby::CompiledScript#initialize(filename, class_name, content, bytes)
return CompiledScript.newInstance(context, new IRubyObject[] {
filename,
runtime.newString(scope.getName()),
content,
Java.getInstance(runtime, bytes)
}, Block.NULL_BLOCK);
}

}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -61,9 +61,9 @@
public abstract class IRScope implements ParseResult {
public static final Logger LOG = LoggerFactory.getLogger(IRScope.class);

private static final Collection<IRClosure> NO_CLOSURES = Collections.unmodifiableCollection(new ArrayList<IRClosure>(0));
private static final Collection<IRClosure> NO_CLOSURES = Collections.EMPTY_LIST;

private static AtomicInteger globalScopeCount = new AtomicInteger();
private static final AtomicInteger globalScopeCount = new AtomicInteger();

/** Unique global scope id */
private int scopeId;
69 changes: 14 additions & 55 deletions core/src/main/ruby/jruby/jruby.rb
Original file line number Diff line number Diff line change
@@ -12,6 +12,11 @@ def dereference(obj); end if false
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def runtime; end if false

# Get the current runtime's config.
# Changes to the configuration won't be reflected in the runtime, meant to be read-only.
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def config; end if false

# Run the provided (required) block with the "global runtime" set to the current runtime,
# for libraries that expect to operate against the global runtime.
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
@@ -22,64 +27,18 @@ def with_current_runtime_as_global; end if false
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def set_context_class_loader(loader = nil); end if false

DEFAULT_FILENAME = '-'.dup; private_constant :DEFAULT_FILENAME
class org::jruby::Ruby
java_alias :parse_bytelist, :parse, [org.jruby.util.ByteList, java.lang.String, org.jruby.runtime.DynamicScope, Java::int, Java::boolean]
end

# Parse the given block or the provided content, returning a JRuby AST node.
def parse(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, lineno = 0, &block)
if block
body = reference0(block).body

if org.jruby.runtime.CompiledBlock === body
raise ArgumentError, "cannot get parse tree from compiled block"
end

body.body_node
else
content = content.to_str
filename = filename.to_str unless filename.equal?(DEFAULT_FILENAME)

if content.encoding == Encoding::ASCII_8BIT
# binary content, parse as though from a stream
runtime.parse_file(filename, java.io.ByteArrayInputStream.new(content.to_java_bytes), nil)
else
runtime.parse_bytelist reference0(content).byte_list, filename, nil, lineno, extra_position_info
end
end
end
alias ast_for parse

def compile_ir(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, &block)
manager = org.jruby.ir.IRManager.new(runtime.instance_config)
manager.dry_run = true
if filename.equal?(DEFAULT_FILENAME)
node = parse(content, &block)
else
node = parse(content, filename, extra_position_info, &block)
end

scope = org.jruby.ir.IRBuilder.build_root(manager, node).scope
scope.top_level_binding_scope = node.scope

scope
end
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def parse(content, filename = '', extra_position_info = false, lineno = 0); end if false

# Parse and compile the given block or provided content, returning a new
# CompiledScript instance.
def compile(content = nil, filename = DEFAULT_FILENAME, extra_position_info = false, &block)
irscope = compile_ir(content, filename, extra_position_info, &block)
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def compile_ir(content, filename = '', extra_position_info = false); end if false

visitor = org.jruby.ir.targets.JVMVisitor.new JRuby.runtime
context = org.jruby.ir.targets.JVMVisitorMethodContext.new
bytes = visitor.compile_to_bytecode(irscope, context)
static_scope = irscope.static_scope;
top_self = JRuby.runtime.top_self
static_scope.module = top_self.class
# Parse and compile the given block or provided content.
# @return JRuby::CompiledScript instance
# @note implemented in *org.jruby.ext.jruby.JRubyLibrary*
def compile(content, filename = '', extra_position_info = false); end if false

CompiledScript.new(filename, irscope.name, content, bytes)
end
end

# NOTE: This is not a public API and is subject to change at our whim.
@@ -128,7 +87,7 @@ def to_s
end

def inspect
"\#<JRuby::CompiledScript #{@name}>"
"\#<#{self.class.name} #{@name}>"
end

def inspect_bytecode
28 changes: 27 additions & 1 deletion spec/java_integration/utilities/jruby_spec.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
require File.dirname(__FILE__) + "/../spec_helper"
require 'jruby'

describe "JRuby#compile" do
it "should produce a CompiledScript instance" do
require 'jruby'
compiled = JRuby.compile("foo = 1")
expect(compiled).to be_kind_of JRuby::CompiledScript
end
end

describe "JRuby::CompiledScript#inspect_bytecode" do
it "should produce a String representation of the compiled bytecode" do
require 'jruby'
compiled = JRuby.compile("foo = 1")
bytecode = compiled.inspect_bytecode

expect(bytecode).to be_kind_of String
end
end

describe "JRuby#compile_ir" do
it "should return an IR script body" do
require 'jruby'
compiled = JRuby.compile_ir("foo = 1; bar = 2", 'foobar.rbx')

expect(compiled.file_name).to eql 'foobar.rbx'
end
end

describe "JRuby#parse" do
it "as bytes" do
require 'jruby'
node = JRuby.parse('bar = :bar; bar.to_s * 111'.force_encoding('ASCII-8BIT'), '')

expect(node).to be_kind_of org.jruby.ast.RootNode
end

it "as string" do
require 'jruby'
node = JRuby.parse('baz = :baz; baz.to_s + " "'.force_encoding('UTF-8'), '')

expect(node).to be_kind_of org.jruby.ast.RootNode
end
end