Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Don't crash on NoMethodError when Perl is involved #224

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
27 changes: 16 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,22 @@ endif()

project(yast2-ruby-bindings)
set(PACKAGE "yast2-ruby-bindings")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x -g -O3 -Wall -Woverloaded-virtual")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu++0x -g -O3 -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x -g3 -O3 -Wall -Woverloaded-virtual")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu++0x -g3 -O3 -Wall")

#
# Where is Ruby ?
#

find_package(Ruby REQUIRED)
if(NOT RUBY_EXECUTABLE)
message(FATAL_ERROR "Ruby not found.")
endif()

# RUBY_BIN_PATH contains the full path including the version suffix,
# e.g. /usr/bin/ruby.ruby2.5
EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig.ruby"
OUTPUT_VARIABLE RUBY_BIN_PATH)

#
# Where is YaST ?
Expand All @@ -26,15 +40,6 @@ if(NOT YAST_PLUGIN_UI_LIBRARY)
message(FATAL_ERROR "YAST_PLUGIN_UI_LIBRARY not set, please install yast2-ycp-ui-bindings-devel.")
endif()

#
# Where is Ruby ?
#

find_package(Ruby REQUIRED)
if(NOT RUBY_EXECUTABLE)
message(FATAL_ERROR "Ruby not found.")
endif()

#
# crypt.h or xcrypt.h ?
#
Expand Down
69 changes: 0 additions & 69 deletions cmake/modules/FindRuby.cmake

This file was deleted.

13 changes: 5 additions & 8 deletions src/binary/Y2RubyComponent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ as published by the Free Software Foundation; either version
#include "Y2RubyComponent.h"
#include "YRuby.h"
#include "YRubyNamespace.h"
#include "Y2RubyUtils.h"

using std::string;
using std::map;

Expand Down Expand Up @@ -88,18 +90,13 @@ Y2Namespace *Y2RubyComponent::import (const char* name)
}
else // report more verbose why require failed
{
VALUE exception = rb_errinfo(); /* get last exception */
pair<string, string> exc = exception_message_and_backtrace();
rb_set_errinfo(Qnil); // clear exception, so we can recover from it
VALUE reason = rb_funcall(exception, rb_intern("message"), 0 );
VALUE trace = rb_funcall(exception, rb_intern("backtrace"), 0 );
VALUE trace_to_s = rb_funcall(trace, rb_intern("join"), 1, rb_str_new_cstr("\n"));
string reason_s(StringValuePtr(reason));
string trace_s(StringValuePtr(trace_to_s));

y2error("Reporting runtime error for import of module '%s' message '%s'",
name, reason_s.c_str());
name, exc.first.c_str());

Y2Namespace * res = new Y2ErrorNamespace (reason_s, trace_s);
Y2Namespace * res = new Y2ErrorNamespace (exc.first, exc.second);
namespaces[name] = res;
return res;
}
Expand Down
22 changes: 16 additions & 6 deletions src/binary/Y2RubyUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,28 @@

using namespace std;

pair<string, string> exception_message_and_backtrace()
{
VALUE exception = rb_errinfo(); /* get last exception */

VALUE reason = rb_funcall(exception, rb_intern("message"), 0 );
string reason_s(StringValuePtr(reason));

VALUE trace = rb_funcall(exception, rb_intern("backtrace"), 0 );
VALUE trace_to_s = rb_funcall(trace, rb_intern("join"), 1, rb_str_new_cstr("\n"));
string trace_s(StringValuePtr(trace_to_s));

return make_pair(reason_s, trace_s);
}

bool y2_require(const char *str)
{
int error;
rb_protect( (VALUE (*)(VALUE))rb_require, (VALUE) str, &error);
if (error)
{
VALUE exception = rb_errinfo(); /* get last exception */
// do not clear exception yet, as it can be also processed later
VALUE reason = rb_funcall(exception, rb_intern("message"), 0 );
VALUE trace = rb_funcall(exception, rb_intern("backtrace"), 0 );
VALUE backtrace = RARRAY_LEN(trace)>0 ? rb_ary_entry(trace, 0) : rb_str_new2("Unknown");
y2error("cannot require yast:%s at %s", StringValuePtr(reason),StringValuePtr(backtrace));
pair<string, string> exc = exception_message_and_backtrace();
y2error("cannot require yast:%s at %s", exc.first.c_str(), exc.second.c_str());
return false;
}

Expand Down
5 changes: 5 additions & 0 deletions src/binary/Y2RubyUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ as published by the Free Software Foundation; either version
*/
VALUE y2ruby_nested_const_get(const std::string &name);

/**
* call rb_errinfo and return its .message and .backtrace.join("\n")
*/
std::pair<std::string, std::string> exception_message_and_backtrace();

/**
* safe variant of rb_require: if an exception happens then log it
*/
Expand Down
29 changes: 16 additions & 13 deletions src/binary/YRuby.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,8 @@ YCPValue YRuby::callInner (string module_name, string function,
if (module == Qnil)
{
y2error ("The Ruby module '%s' is not loaded.", full_name.c_str());
VALUE exception = rb_gv_get("$!"); /* get last exception */
VALUE reason = rb_funcall(exception, rb_intern("message"), 0 );
VALUE trace = rb_gv_get("$@"); /* get last exception trace */
VALUE backtrace = RARRAY_LEN(trace)>0 ? rb_ary_entry(trace, 0) : rb_str_new2("Unknown");
y2error("%s load failed:%s at %s", full_name.c_str(), StringValuePtr(reason), StringValuePtr(backtrace));
pair<string, string> exc = exception_message_and_backtrace();
y2error("%s load failed:%s at %s", full_name.c_str(), exc.first.c_str(), exc.second.c_str());
return YCPVoid();
}

Expand All @@ -203,7 +200,7 @@ YCPValue YRuby::callInner (string module_name, string function,
rb_gc_register_address(values + i + 3);
}

y2debug( "Will call function '%s' in module '%s' with '%d' arguments", function.c_str(), module_name.c_str(), size-1);
y2debug( "Will call function '%s' in module '%s' with %d arguments", function.c_str(), module_name.c_str(), size);

int error;
VALUE result = rb_protect(protected_call, (VALUE)values, &error);
Expand All @@ -214,17 +211,23 @@ YCPValue YRuby::callInner (string module_name, string function,

if (error)
{
VALUE exception = rb_gv_get("$!"); /* get last exception */
VALUE reason = rb_funcall(exception, rb_intern("message"), 0 );
VALUE trace = rb_gv_get("$@"); /* get last exception trace */
VALUE backtrace = RARRAY_LEN(trace)>0 ? rb_ary_entry(trace, 0) : rb_str_new2("Unknown");
y2error("%s.%s failed:%s at %s", module_name.c_str(), function.c_str(), StringValuePtr(reason),StringValuePtr(backtrace));
pair<string, string> exc = exception_message_and_backtrace();
const string& reason = exc.first;
y2error("%s.%s failed:%s at %s", module_name.c_str(), function.c_str(), reason.c_str(), exc.second.c_str());
//workaround if last_exception failed, then return always string with message
if(function == "last_exception") //TODO constantify last_exception
{
return YCPString(StringValuePtr(reason));
return YCPString(reason);
}
set_last_exception(module,StringValuePtr(reason));
set_last_exception(module, reason);

VALUE exhash = rb_hash_new();
rb_hash_aset(exhash, rb_str_new2("message"), rb_str_new2(exc.first.c_str()));
rb_hash_aset(exhash, rb_str_new2("backtrace"), rb_str_new2(exc.second.c_str()));
VALUE yast_module = y2ruby_nested_const_get("Yast");
rb_funcall(yast_module, rb_intern("call_yast_function"), 3,
rb_str_new2("Y2Exception"), rb_str_new2("exception"), exhash);

return YCPVoid();
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/ruby/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/yast.rb DESTINATION ${RUBY_VENDORLIB_D
INSTALL(FILES ${files} DESTINATION ${RUBY_VENDORLIB_DIR}/yast)
INSTALL(FILES ${rspec} DESTINATION ${RUBY_VENDORLIB_DIR}/yast/rspec)
INSTALL(FILES ${core_ext} DESTINATION ${RUBY_VENDORLIB_DIR}/yast/core_ext)

INSTALL(FILES yast/y2_exception.rb DESTINATION /usr/share/YaST2/modules)
15 changes: 15 additions & 0 deletions src/ruby/yast/y2_exception.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "yast"

module Yast
# For passing exceptions through liby2
class Y2ExceptionClass < Module
def main
@exception = {}
end

publish variable: :exception, type: "map"
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general change looks OK for me, I just miss more documentation in this module. Especially use cases ( exceptions when calling ruby code from non-ruby parts (includes e.g. also pkg bindings) ), how that variable exception looks like and who can set it. And maybe a paragraph in readme would make sense to point to this.


Y2Exception = Y2ExceptionClass.new
Y2Exception.main
end