Skip to content

Commit 887b5bf

Browse files
committed
[GR-22583] C handle specs
PullRequest: truffleruby/3359
2 parents c49f2db + a53e99b commit 887b5bf

File tree

12 files changed

+193
-2
lines changed

12 files changed

+193
-2
lines changed

spec/mspec/lib/mspec/runner/context.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ def process
210210
MSpec.clear_expectations
211211
if example
212212
passed = protect nil, example
213+
passed = protect nil, -> { MSpec.actions :passed, state, example } if passed
213214
MSpec.actions :example, state, example
214215
protect nil, EXPECTATION_MISSING if !MSpec.expectation? and passed
215216
end

spec/mspec/lib/mspec/runner/mspec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module MSpec
2626
@unload = nil
2727
@tagged = nil
2828
@current = nil
29+
@passed = nil
2930
@example = nil
3031
@modes = []
3132
@shared = {}
@@ -243,6 +244,7 @@ def self.store(symbol, value)
243244
# :before before a single spec is run
244245
# :add while a describe block is adding examples to run later
245246
# :expectation before a 'should', 'should_receive', etc.
247+
# :passed after an example block is run and passes, passed the block, run before :example action
246248
# :example after an example block is run, passed the block
247249
# :exception after an exception is rescued
248250
# :after after a single spec is run

spec/nativeconversion.mspec

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
class NativeHandleChecker
3+
EXPECTED_FAILURES = {
4+
"C-API Debug function rb_debug_inspector_open creates a debug context and calls the given callback" => 2,
5+
"C-API Debug function rb_debug_inspector_frame_self_get returns self" => 2,
6+
"C-API Debug function rb_debug_inspector_frame_class_get returns the frame class" => 2,
7+
"C-API Debug function rb_debug_inspector_frame_binding_get returns the current binding" => 2,
8+
"C-API Debug function rb_debug_inspector_frame_binding_get matches the locations in rb_debug_inspector_backtrace_locations" => 2,
9+
"C-API Debug function rb_debug_inspector_frame_iseq_get returns an InstructionSequence" => 2,
10+
"C-API Debug function rb_debug_inspector_backtrace_locations returns an array of Thread::Backtrace::Location" => 2,
11+
"C-API IO function rb_io_printf calls #to_str to convert the format object to a String" => 2,
12+
"C-API IO function rb_io_printf calls #to_s to convert the object to a String" => 2,
13+
"C-API IO function rb_io_printf writes the Strings to the IO" => 4,
14+
"C-API IO function rb_io_print calls #to_s to convert the object to a String" => 1,
15+
"C-API IO function rb_io_print writes the Strings to the IO with no separator" => 3,
16+
"C-API IO function rb_io_puts calls #to_s to convert the object to a String" => 1,
17+
"C-API IO function rb_io_puts writes the Strings to the IO separated by newlines" => 3,
18+
"C-API Kernel function rb_rescue executes the passed 'rescue function' if a StandardError exception is raised" => 3,
19+
"C-API Kernel function rb_rescue passes the user supplied argument to the 'rescue function' if a StandardError exception is raised" => 4,
20+
"C-API Kernel function rb_rescue passes the raised exception to the 'rescue function' if a StandardError exception is raised" => 2,
21+
"C-API Kernel function rb_rescue raises an exception if any exception is raised inside the 'rescue function'" => 1,
22+
"C-API Kernel function rb_rescue makes $! available only during the 'rescue function' execution" => 1,
23+
"CApiObject rb_obj_call_init sends #initialize" => 2,
24+
"C-API String function rb_sprintf formats a string VALUE using to_s if sign not specified in format" => 1,
25+
"C-API String function rb_sprintf formats a string VALUE using inspect if sign specified in format" => 1,
26+
"C-API String function rb_sprintf formats a TrueClass VALUE as `TrueClass` if sign not specified in format" => 1,
27+
"C-API String function rb_sprintf truncates a VALUE string to a supplied precision if that is shorter than the VALUE string" => 1,
28+
"C-API String function rb_sprintf does not truncates a VALUE string to a supplied precision if that is longer than the VALUE string" => 1,
29+
"C-API String function rb_sprintf pads a VALUE string to a supplied width if that is longer than the VALUE string" => 1,
30+
"C-API String function rb_sprintf can format a string VALUE as a pointer and gives the same output as sprintf in C" => 1,
31+
"C-API String function rb_string_value_cstr returns a non-null pointer for a simple string" => 1,
32+
"C-API String function rb_string_value_cstr returns a non-null pointer for a UTF-16 string" => 1,
33+
"C-API String function rb_string_value_cstr raises an error if a string contains a null" => 1,
34+
"C-API String function rb_string_value_cstr raises an error if a UTF-16 string contains a null" => 1,
35+
"CApiWrappedTypedStruct throws an exception for a wrong type" => 1,
36+
"C-API Util function rb_scan_args assigns Hash arguments" => 1,
37+
"C-API Util function rb_scan_args assigns required and Hash arguments" => 1,
38+
"C-API Util function rb_scan_args assigns required, optional, splat, post-splat, Hash and block arguments" => 1,
39+
"C-API Util function rb_get_kwargs extracts required arguments in the order requested" => 2,
40+
"C-API Util function rb_get_kwargs extracts required and optional arguments in the order requested" => 1,
41+
"C-API Util function rb_get_kwargs raises an error if a required argument is not in the hash" => 1,
42+
"Native handle conversion converts all elements to native handles when memcpying an RARRAY_PTR" => 1000,
43+
}
44+
45+
def register
46+
MSpec.register :before, self
47+
MSpec.register :passed, self
48+
end
49+
50+
def unregister
51+
MSpec.unregister :before, self
52+
MSpec.unregister :passed, self
53+
end
54+
55+
def before(state)
56+
@start_count = Truffle::Debug.cexts_to_native_count
57+
end
58+
59+
def passed(state, example)
60+
(Truffle::Debug.cexts_to_native_count - @start_count).should == (EXPECTED_FAILURES[state.description] || 0)
61+
end
62+
end
63+
64+
begin
65+
checker = NativeHandleChecker.new
66+
checker.register
67+
load File.expand_path('../truffleruby.mspec', __FILE__)
68+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
#include "ruby.h"
11+
#include "rubyspec.h"
12+
#include <stdio.h>
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
static VALUE value_comparison_with_nil(VALUE self, VALUE val) {
19+
return val == Qnil ? Qtrue : Qfalse;
20+
}
21+
22+
static VALUE value_array_ptr_access(VALUE self, VALUE ary) {
23+
VALUE *ptr = RARRAY_PTR(ary);
24+
return ptr[0];
25+
}
26+
27+
static VALUE value_array_ptr_memcpy(VALUE self, VALUE ary) {
28+
VALUE *ptr = RARRAY_PTR(ary);
29+
VALUE *cpy = malloc(RARRAY_LEN(ary) * sizeof(VALUE));
30+
VALUE res;
31+
memcpy(cpy, ptr, RARRAY_LEN(ary) * sizeof(VALUE));
32+
res = cpy[1];
33+
free(cpy);
34+
return res;
35+
}
36+
37+
static VALUE our_static_value;
38+
39+
static VALUE value_store_in_static(VALUE self, VALUE val) {
40+
our_static_value = val;
41+
return val;
42+
}
43+
44+
void Init_handle_conversion_spec(void) {
45+
VALUE cls = rb_define_class("CAPIHandleConversionTest", rb_cObject);
46+
rb_define_method(cls, "value_comparison_with_nil", value_comparison_with_nil, 1);
47+
rb_define_method(cls, "value_array_ptr_access", value_array_ptr_access, 1);
48+
rb_define_method(cls, "value_array_ptr_memcpy", value_array_ptr_memcpy, 1);
49+
rb_define_method(cls, "value_store_in_static", value_store_in_static, 1);
50+
}
51+
52+
#ifdef __cplusplus
53+
}
54+
#endif
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 2.0, or
6+
# GNU General Public License version 2, or
7+
# GNU Lesser General Public License version 2.1.
8+
9+
require_relative '../../ruby/optional/capi/spec_helper'
10+
11+
load_extension('handle_conversion')
12+
13+
guard -> { Truffle::Boot.get_option('cexts-to-native-count') == true } do
14+
describe "Native handle conversion" do
15+
it "converts no handles when comparing a VALUE with a constant" do
16+
CAPIHandleConversionTest.new.value_comparison_with_nil(Object.new)
17+
end
18+
19+
it "converts no handles when accessing array elements via an RARRAY_PTR" do
20+
ary = Array.new(1000) { Object.new }
21+
CAPIHandleConversionTest.new.value_array_ptr_access([ary])
22+
end
23+
24+
it "converts all elements to native handles when memcpying an RARRAY_PTR" do
25+
ary = Array.new(1000) { Object.new }
26+
CAPIHandleConversionTest.new.value_array_ptr_memcpy(ary)
27+
end
28+
29+
it "converts no handles when storing a VALUE in a static variable" do
30+
CAPIHandleConversionTest.new.value_store_in_static(Object.new)
31+
end
32+
end
33+
end

src/main/java/org/truffleruby/cext/ValueWrapperManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ protected static long allocateHandle(ValueWrapper wrapper, RubyContext context,
324324
block = holder.handleBlock;
325325
}
326326

327-
if (context.getOptions().CEXTS_TO_NATIVE_STATS) {
327+
if (context.getOptions().CEXTS_TO_NATIVE_COUNT) {
328328
context.getValueWrapperManager().recordHandleAllocation();
329329
}
330330

src/main/java/org/truffleruby/debug/TruffleDebugNodes.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,4 +1275,13 @@ protected RubyArray primitiveNames() {
12751275
}
12761276
}
12771277

1278+
@CoreMethod(names = "cexts_to_native_count", onSingleton = true)
1279+
public abstract static class HandleCreationCountNode extends CoreMethodArrayArgumentsNode {
1280+
1281+
@Specialization
1282+
protected long handleCount() {
1283+
return getContext().getValueWrapperManager().totalHandleAllocations();
1284+
}
1285+
}
1286+
12781287
}

src/main/java/org/truffleruby/options/Options.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ public class Options {
164164
public final boolean ROPE_PRINT_INTERN_STATS;
165165
/** --cexts-to-native-stats=false */
166166
public final boolean CEXTS_TO_NATIVE_STATS;
167+
/** --cexts-to-native-count=CEXTS_TO_NATIVE_STATS */
168+
public final boolean CEXTS_TO_NATIVE_COUNT;
167169
/** --basic-ops-log-rewrite=false */
168170
public final boolean BASICOPS_LOG_REWRITE;
169171
/** --array-small=3 */
@@ -278,6 +280,7 @@ public Options(Env env, OptionValues options, LanguageOptions languageOptions) {
278280
LOG_PENDING_INTERRUPTS = options.get(OptionsCatalog.LOG_PENDING_INTERRUPTS_KEY);
279281
ROPE_PRINT_INTERN_STATS = options.get(OptionsCatalog.ROPE_PRINT_INTERN_STATS_KEY);
280282
CEXTS_TO_NATIVE_STATS = options.get(OptionsCatalog.CEXTS_TO_NATIVE_STATS_KEY);
283+
CEXTS_TO_NATIVE_COUNT = options.hasBeenSet(OptionsCatalog.CEXTS_TO_NATIVE_COUNT_KEY) ? options.get(OptionsCatalog.CEXTS_TO_NATIVE_COUNT_KEY) : CEXTS_TO_NATIVE_STATS;
281284
BASICOPS_LOG_REWRITE = options.get(OptionsCatalog.BASICOPS_LOG_REWRITE_KEY);
282285
ARRAY_SMALL = options.get(OptionsCatalog.ARRAY_SMALL_KEY);
283286
CEXTS_MARKING_CACHE = options.get(OptionsCatalog.CEXTS_MARKING_CACHE_KEY);
@@ -443,6 +446,8 @@ public Object fromDescriptor(OptionDescriptor descriptor) {
443446
return ROPE_PRINT_INTERN_STATS;
444447
case "ruby.cexts-to-native-stats":
445448
return CEXTS_TO_NATIVE_STATS;
449+
case "ruby.cexts-to-native-count":
450+
return CEXTS_TO_NATIVE_COUNT;
446451
case "ruby.basic-ops-log-rewrite":
447452
return BASICOPS_LOG_REWRITE;
448453
case "ruby.array-small":

src/options.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ INTERNAL: # Options for debugging the TruffleRuby implementation
172172
LOG_PENDING_INTERRUPTS: [log-pending-interrupts, boolean, false, Log when executing pending interrupts]
173173
ROPE_PRINT_INTERN_STATS: [rope-print-intern-stats, boolean, false, Print interned rope stats at application exit]
174174
CEXTS_TO_NATIVE_STATS: [cexts-to-native-stats, boolean, false, Track the number of conversions of VALUEs to native and print the stats at application exit]
175+
CEXTS_TO_NATIVE_COUNT: [cexts-to-native-count, boolean, CEXTS_TO_NATIVE_STATS, Track the number of conversions of VALUEs to native]
175176

176177
# Options to debug the implementation
177178
LAZY_BUILTINS: [lazy-builtins, boolean, LAZY_CALLTARGETS, Load builtin classes (core methods & primitives) lazily on first use]

src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public class OptionsCatalog {
102102
public static final OptionKey<Boolean> LOG_PENDING_INTERRUPTS_KEY = new OptionKey<>(false);
103103
public static final OptionKey<Boolean> ROPE_PRINT_INTERN_STATS_KEY = new OptionKey<>(false);
104104
public static final OptionKey<Boolean> CEXTS_TO_NATIVE_STATS_KEY = new OptionKey<>(false);
105+
public static final OptionKey<Boolean> CEXTS_TO_NATIVE_COUNT_KEY = new OptionKey<>(CEXTS_TO_NATIVE_STATS_KEY.getDefaultValue());
105106
public static final OptionKey<Boolean> LAZY_BUILTINS_KEY = new OptionKey<>(LAZY_CALLTARGETS_KEY.getDefaultValue());
106107
public static final OptionKey<Boolean> LAZY_TRANSLATION_CORE_KEY = new OptionKey<>(LAZY_CALLTARGETS_KEY.getDefaultValue());
107108
public static final OptionKey<Boolean> CHAOS_DATA_KEY = new OptionKey<>(false);
@@ -819,6 +820,14 @@ public class OptionsCatalog {
819820
.usageSyntax("")
820821
.build();
821822

823+
public static final OptionDescriptor CEXTS_TO_NATIVE_COUNT = OptionDescriptor
824+
.newBuilder(CEXTS_TO_NATIVE_COUNT_KEY, "ruby.cexts-to-native-count")
825+
.help("Track the number of conversions of VALUEs to native")
826+
.category(OptionCategory.INTERNAL)
827+
.stability(OptionStability.EXPERIMENTAL)
828+
.usageSyntax("")
829+
.build();
830+
822831
public static final OptionDescriptor LAZY_BUILTINS = OptionDescriptor
823832
.newBuilder(LAZY_BUILTINS_KEY, "ruby.lazy-builtins")
824833
.help("Load builtin classes (core methods & primitives) lazily on first use")
@@ -1465,6 +1474,8 @@ public static OptionDescriptor fromName(String name) {
14651474
return ROPE_PRINT_INTERN_STATS;
14661475
case "ruby.cexts-to-native-stats":
14671476
return CEXTS_TO_NATIVE_STATS;
1477+
case "ruby.cexts-to-native-count":
1478+
return CEXTS_TO_NATIVE_COUNT;
14681479
case "ruby.lazy-builtins":
14691480
return LAZY_BUILTINS;
14701481
case "ruby.lazy-translation-core":
@@ -1674,6 +1685,7 @@ public static OptionDescriptor[] allDescriptors() {
16741685
LOG_PENDING_INTERRUPTS,
16751686
ROPE_PRINT_INTERN_STATS,
16761687
CEXTS_TO_NATIVE_STATS,
1688+
CEXTS_TO_NATIVE_COUNT,
16771689
LAZY_BUILTINS,
16781690
LAZY_TRANSLATION_CORE,
16791691
CHAOS_DATA,

0 commit comments

Comments
 (0)