Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ Other documentation in this repository and in the wiki explain in more detail ho

Other `test` and `spec` suites may also be appropriate depending on what functionality you are working on.

### Linking Java code to CRuby

When a method, class or field implements or ports a CRuby C function or macro, annotate it with `@CRuby` (`org.jruby.anno.CRuby`) rather than writing a `// MRI: rb_foo` comment:

```java
@CRuby(value = "match_begin", file = "re.c")
@JRubyMethod
public IRubyObject begin(ThreadContext context, IRubyObject index) {
```

`file` is optional. Repeat the annotation when one Java method covers several C functions, and use `note` for qualifiers such as `note = "first half"`.

## Testing

See [BUILDING.md](BUILDING.md) for more details.
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/java/org/jruby/RubyMatchData.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.joni.Region;
import org.joni.exception.JOniException;
import org.joni.exception.ValueException;
import org.jruby.anno.CRuby;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.api.Convert;
Expand Down Expand Up @@ -558,7 +559,7 @@ public static int backrefNumber(ThreadContext context, Regex pattern, Region reg
return toInt(context, obj);
}

// MRI: namev_to_backref_number
@CRuby(value = "namev_to_backref_number", file = "re.c")
private int namevToBackrefNumber(ThreadContext context, IRubyObject name) {
int num = -1;

Expand All @@ -583,7 +584,7 @@ private int nameToBackrefError(ThreadContext context, String name) {
throw indexError(context, "undefined group name reference " + name);
}

// MRI: match_ary_subseq
@CRuby(value = "match_ary_subseq", file = "re.c")
private IRubyObject matchArySubseq(ThreadContext context, int beg, int len, RubyArray result) {
assert result != null;

Expand All @@ -606,7 +607,7 @@ private IRubyObject matchArySubseq(ThreadContext context, int beg, int len, Ruby
return result;
}

// MRI: match_ary_aref
@CRuby(value = "match_ary_aref", file = "re.c")
private IRubyObject matchAryAref(ThreadContext context, IRubyObject index, RubyArray result) {
int[] begLen = new int[2];
int numRegs = regs.getNumRegs();
Expand Down Expand Up @@ -678,9 +679,7 @@ public IRubyObject size(ThreadContext context) {
return regs == null ? RubyFixnum.one(context.runtime) : asFixnum(context, regs.getNumRegs());
}

/**
* MRI: match_begin
*/
@CRuby(value = "match_begin", file = "re.c")
@JRubyMethod
public IRubyObject begin(ThreadContext context, IRubyObject index) {
check(context);
Expand Down
81 changes: 81 additions & 0 deletions core/src/main/java/org/jruby/anno/CRuby.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v20.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby.anno;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Links a Java declaration to the CRuby C function or macro it implements or was ported from.
*
* <pre>
* &#64;CRuby("match_size")
* &#64;CRuby(value = "match_begin", file = "re.c")
* &#64;CRuby(value = "ary_reject_bang", file = "array.c")
* &#64;CRuby(value = "reject_bang_i", file = "array.c")
* &#64;CRuby(value = "rb_str_count", note = "first half")
* </pre>
*
* Use it in place of a {@code // MRI: rb_foo} comment or a Javadoc that only names the C function, so tools can
* audit the mapping and documentation can link to the C source. It is retained at runtime so that tools working from a
* live JRuby (IRB, RI, language servers) can find it by reflecting on the Java method behind a Ruby method.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.FIELD})
@Repeatable(CRuby.List.class)
public @interface CRuby {
/**
* The C function or macro, e.g. "match_integer_at".
*/
String value();

/**
* The C source file defining it, relative to the CRuby root, e.g. "re.c". Optional, mainly useful when the
* name is defined in more than one file.
*/
String file() default "";

/**
* Free text qualifying the link, e.g. "first half" or "loop body".
*/
String note() default "";

/**
* Container for repeated {@link CRuby} annotations; not used directly.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.FIELD})
@interface List {
CRuby[] value();
}
}
Loading