Skip to content

Commit c7fecb3

Browse files
committed
Add test for failing ruby scripts, now it does not segfaults anymore, and shows the output of the backtrace.
1 parent 97a01c4 commit c7fecb3

File tree

8 files changed

+310
-23
lines changed

8 files changed

+310
-23
lines changed

source/loaders/rb_loader/source/rb_loader_impl.c

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ typedef struct loader_impl_rb_function_type
5858

5959
} * loader_impl_rb_function;
6060

61+
typedef struct loader_impl_rb_module_eval_protect_type
62+
{
63+
int argc;
64+
VALUE * argv;
65+
VALUE module;
66+
} * loader_impl_rb_module_eval_protect;
67+
6168
int function_rb_interface_create(function func, function_impl impl)
6269
{
6370
signature s = function_signature(func);
@@ -515,17 +522,31 @@ VALUE rb_loader_impl_load_data(loader_impl impl, const loader_naming_path path)
515522
return Qnil;
516523
}
517524

525+
VALUE rb_loader_impl_module_eval_protect(VALUE args)
526+
{
527+
/* TODO: Do this properly */
528+
loader_impl_rb_module_eval_protect protect = (loader_impl_rb_module_eval_protect)args;
529+
530+
return rb_mod_module_eval(protect->argc, protect->argv, protect->module);
531+
}
532+
518533
VALUE rb_loader_impl_module_eval(VALUE module, VALUE module_data)
519534
{
520535
const int argc = 1;
521536
VALUE result;
522-
VALUE args[argc];
537+
VALUE argv[argc];
538+
struct loader_impl_rb_module_eval_protect_type protect;
539+
int state;
540+
541+
argv[0] = module_data;
523542

524-
args[0] = module_data;
543+
protect.argc = argc;
544+
protect.argv = argv;
545+
protect.module = module;
525546

526-
result = rb_mod_module_eval(1, args, module);
547+
result = rb_protect(rb_loader_impl_module_eval_protect, (VALUE)&protect, &state);
527548

528-
if (result == Qnil)
549+
if (state || result == Qnil)
529550
{
530551
VALUE exception;
531552

@@ -544,11 +565,13 @@ VALUE rb_loader_impl_module_eval(VALUE module, VALUE module_data)
544565
backtrace = rb_funcall(exception, rb_intern("backtrace"), 0);
545566

546567
rb_io_puts(1, &backtrace, rb_stderr);
547-
548-
rb_raise(rb_eLoadError, "Invalid module evaluation");
549-
550-
return Qnil;
551568
}
569+
else
570+
{
571+
log_write("metacall", LOG_LEVEL_ERROR, "Ruby module backtrace not available");
572+
}
573+
574+
return Qnil;
552575
}
553576

554577
return result;
@@ -604,12 +627,6 @@ loader_impl_rb_module rb_loader_impl_load_from_file_module(loader_impl impl, con
604627
return rb_module;
605628
}
606629
}
607-
else
608-
{
609-
VALUE exception = rb_errinfo();
610-
611-
log_write("metacall", LOG_LEVEL_DEBUG, "Ruby loader error (%s)", RSTRING_PTR(exception));
612-
}
613630
}
614631
}
615632

@@ -654,11 +671,23 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_namin
654671
if (rb_module == NULL)
655672
{
656673
log_write("metacall", LOG_LEVEL_ERROR, "Invalid ruby module loading %s", paths[iterator]);
657-
658-
continue;
659674
}
675+
else
676+
{
677+
vector_push_back(handle->modules, &rb_module);
678+
}
679+
}
680+
681+
// Do not load the handle in case there isn't modules
682+
if (vector_size(handle->modules) == 0)
683+
{
684+
log_write("metacall", LOG_LEVEL_ERROR, "No module could be loaded");
685+
686+
vector_destroy(handle->modules);
687+
688+
free(handle);
660689

661-
vector_push_back(handle->modules, &rb_module);
690+
return NULL;
662691
}
663692

664693
return (loader_handle)handle;
@@ -717,12 +746,6 @@ loader_impl_rb_module rb_loader_impl_load_from_memory_module(loader_impl impl, c
717746
return rb_module;
718747
}
719748
}
720-
else
721-
{
722-
VALUE exception = rb_errinfo();
723-
724-
log_write("metacall", LOG_LEVEL_DEBUG, "Ruby loader error (%s)", RSTRING_PTR(exception));
725-
}
726749
}
727750
}
728751

source/scripts/ruby/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ add_subdirectory(second)
1818
add_subdirectory(blog)
1919
add_subdirectory(cache)
2020
add_subdirectory(ducktype)
21+
add_subdirectory(invalid)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#
2+
# Configure ruby project
3+
#
4+
5+
rb_project(invalid 0.1.0)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#! /usr/bin/env ruby
2+
3+
require 'nokogiri'
4+
require 'open-uri'
5+
6+
def nokogiri_example()
7+
# Fetch and parse HTML document
8+
doc = Nokogiri::HTML(open('https://nokogiri.org/tutorials/installing_nokogiri.html'))
9+
10+
puts "### Search for nodes by css"
11+
doc.css('nav ul.menu li a', 'article h2').each do |link|
12+
puts link.content
13+
end
14+
15+
puts "### Search for nodes by xpath"
16+
doc.xpath('//nav//ul//li/a', '//article//h2').each do |link|
17+
puts link.content
18+
end
19+
20+
puts "### Or mix and match."
21+
doc.search('nav ul.menu li a', '//article//h2').each do |link|
22+
puts link.content
23+
end
24+
25+
return 3
26+
end

source/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,4 @@ add_subdirectory(metacall_reinitialize_test)
142142
add_subdirectory(metacall_fork_test)
143143
add_subdirectory(metacall_return_monad_test)
144144
add_subdirectory(metacall_callback_test)
145+
add_subdirectory(metacall_ruby_fail_test)
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#
2+
# Executable name and options
3+
#
4+
5+
# Target name
6+
set(target metacall-ruby-fail-test)
7+
message(STATUS "Test ${target}")
8+
9+
#
10+
# Compiler warnings
11+
#
12+
13+
include(Warnings)
14+
15+
#
16+
# Compiler security
17+
#
18+
19+
include(SecurityFlags)
20+
21+
#
22+
# Sources
23+
#
24+
25+
set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}")
26+
set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source")
27+
28+
set(sources
29+
${source_path}/main.cpp
30+
${source_path}/metacall_ruby_fail_test.cpp
31+
)
32+
33+
# Group source files
34+
set(header_group "Header Files (API)")
35+
set(source_group "Source Files")
36+
source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$"
37+
${header_group} ${headers})
38+
source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$"
39+
${source_group} ${sources})
40+
41+
#
42+
# Create executable
43+
#
44+
45+
# Build executable
46+
add_executable(${target}
47+
${sources}
48+
)
49+
50+
# Create namespaced alias
51+
add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target})
52+
53+
#
54+
# Project options
55+
#
56+
57+
set_target_properties(${target}
58+
PROPERTIES
59+
${DEFAULT_PROJECT_OPTIONS}
60+
FOLDER "${IDE_FOLDER}"
61+
)
62+
63+
#
64+
# Include directories
65+
#
66+
67+
target_include_directories(${target}
68+
PRIVATE
69+
${DEFAULT_INCLUDE_DIRECTORIES}
70+
${PROJECT_BINARY_DIR}/source/include
71+
)
72+
73+
#
74+
# Libraries
75+
#
76+
77+
target_link_libraries(${target}
78+
PRIVATE
79+
${DEFAULT_LIBRARIES}
80+
81+
GTest
82+
83+
${META_PROJECT_NAME}::version
84+
${META_PROJECT_NAME}::preprocessor
85+
${META_PROJECT_NAME}::environment
86+
${META_PROJECT_NAME}::format
87+
${META_PROJECT_NAME}::log
88+
${META_PROJECT_NAME}::memory
89+
${META_PROJECT_NAME}::portability
90+
${META_PROJECT_NAME}::adt
91+
${META_PROJECT_NAME}::reflect
92+
${META_PROJECT_NAME}::dynlink
93+
${META_PROJECT_NAME}::detour
94+
${META_PROJECT_NAME}::serial
95+
${META_PROJECT_NAME}::configuration
96+
${META_PROJECT_NAME}::loader
97+
${META_PROJECT_NAME}::metacall
98+
)
99+
100+
#
101+
# Compile definitions
102+
#
103+
104+
target_compile_definitions(${target}
105+
PRIVATE
106+
${DEFAULT_COMPILE_DEFINITIONS}
107+
)
108+
109+
#
110+
# Compile options
111+
#
112+
113+
target_compile_options(${target}
114+
PRIVATE
115+
${DEFAULT_COMPILE_OPTIONS}
116+
)
117+
118+
#
119+
# Linker options
120+
#
121+
122+
target_link_libraries(${target}
123+
PRIVATE
124+
${DEFAULT_LINKER_OPTIONS}
125+
)
126+
127+
#
128+
# Define test
129+
#
130+
131+
add_test(NAME ${target}
132+
COMMAND $<TARGET_FILE:${target}>
133+
)
134+
135+
#
136+
# Define test properties
137+
#
138+
139+
set_property(TEST ${target}
140+
PROPERTY LABELS ${target} MEMCHECK_IGNORE
141+
)
142+
143+
include(TestEnvironmentVariables)
144+
145+
test_environment_variables(${target}
146+
""
147+
${TESTS_ENVIRONMENT_VARIABLES}
148+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* MetaCall Library by Parra Studios
3+
* A library for providing a foreign function interface calls.
4+
*
5+
* Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia <[email protected]>
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
#include <gmock/gmock.h>
22+
23+
int main(int argc, char * argv[])
24+
{
25+
::testing::InitGoogleMock(&argc, argv);
26+
27+
return RUN_ALL_TESTS();
28+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* MetaCall Library by Parra Studios
3+
* A library for providing a foreign function interface calls.
4+
*
5+
* Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia <[email protected]>
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
#include <gmock/gmock.h>
22+
23+
#include <metacall/metacall.h>
24+
#include <metacall/metacall_value.h>
25+
#include <metacall/metacall_loaders.h>
26+
27+
class metacall_test : public testing::Test
28+
{
29+
public:
30+
};
31+
32+
TEST_F(metacall_test, DefaultConstructor)
33+
{
34+
metacall_print_info();
35+
36+
metacall_log_stdio_type log_stdio = { stdout };
37+
38+
ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio));
39+
40+
ASSERT_EQ((int) 0, (int) metacall_initialize());
41+
42+
/* Ruby */
43+
#if defined(OPTION_BUILD_LOADERS_RB)
44+
{
45+
const char * rb_scripts[] =
46+
{
47+
"invalid.rb"
48+
};
49+
50+
EXPECT_EQ((int) 1, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL));
51+
}
52+
#endif /* OPTION_BUILD_LOADERS_RB */
53+
54+
EXPECT_EQ((int) 0, (int) metacall_destroy());
55+
}

0 commit comments

Comments
 (0)