...
 
Commits (10)
  • Philip Chimento's avatar
    context: Remove lazy flag from evaluated sources · d588f22f
    Philip Chimento authored
    This escaped notice when we removed the lazy flag from all other sources
    in commit b032fda4.
    d588f22f
  • Philip Chimento's avatar
    context: Fix gjs_context_eval() for non-zero-terminated strings · 870b1942
    Philip Chimento authored
    Calling gjs_context_eval() with a non-zero-terminated string has
    apparently been broken for quite a long time. I guess nobody ever does
    that.
    
    This is a surprisingly complicated fix for a simple-sounding problem.
    The complication is due to the passed-in strlen being ignored in more
    than one place: both in gjs_strip_unix_shebang() and in the code that
    converts UTF-8 to UTF-16.
    
    In addition, gjs_strip_unix_shebang() would access invalid memory if
    given a 1-length string or a non-zero-terminated string.
    
    We fix the UTF-16 conversion code, and replace gjs_strip_unix_shebang()
    with a safer version using C++ strings (which we have anyway after
    converting to UTF-16.) This new function, gjs_unix_shebang_len(),
    returns the offset that must be added to the string's starting position,
    in order to skip the shebang line.
    
    It would be better in the future to return a std::u16string_view from
    gjs_unix_shebang_len(), but that is not yet available in C++14.
    
    This bug was found by compiling with -Wunused-parameter!
    870b1942
  • Philip Chimento's avatar
    release: Prepare for 1.56.1 · 2b17ce95
    Philip Chimento authored
    2b17ce95
  • Philip Chimento's avatar
    build: Post-release version bump · 01e98df4
    Philip Chimento authored
    01e98df4
  • Philip Chimento's avatar
    boxed: Track ownership of boxed pointer better · a4888066
    Philip Chimento authored
    This is the second bug of this type that I've fixed since refactoring
    this code, so clearly we need to do something differently. We rename
    m_not_owning_ptr to m_owning_ptr and make sure it starts out false (so
    that we never try to free a pointer if it wasn't assigned yet.) Then we
    introduce two methods for setting the m_ptr field: own_ptr() which sets
    m_owning_ptr, and share_ptr() which does not. We use these methods
    consistently everywhere that we previously set m_ptr directly.
    
    Closes: #240
    a4888066
  • Philip Chimento's avatar
    cairo: Take may_be_null into account in foreign struct conversion · 54a2acdd
    Philip Chimento authored
    Previously, when converting from a Cairo struct to a GIArgument, the
    "may_be_null" parameter would be ignored and a null pointer was always
    accepted (and then would probably crash, although this seems to be an
    unlikely case as we never got any bug reports of it.)
    
    Instead, we do a null check, and throw a JS exception if needed, in the
    foreign struct conversion callbacks in the various Cairo types.
    
    Requires exposing a previously static function in arg.cpp so that the
    thrown exceptions can have more useful messages.
    
    This bug was found with -Wunused-parameter.
    54a2acdd
  • Philip Chimento's avatar
    cairo: Don't free unowned Cairo structs · 4341a904
    Philip Chimento authored
    It seems that in all other release functions, we don't free the
    GIArgument's data in the transfer-none case. That would end up freeing
    a passed-in argument that was owned by the caller, or a return value
    that was owned by the callee.
    
    This bug was spotted by compiling with -Wunused-parameter.
    4341a904
  • Marco Trevisan's avatar
    gobject: Use auto-compartment on object when setting property · 42103be2
    Marco Trevisan authored
    When binding a property inherited from the object to another
    gobject property added in Javascript, it could happen that gjs
    crashes when setting the property.
    
    So enter a new compartment when doing this.
    
    Fixes #246
    42103be2
  • Philip Chimento's avatar
    console: Don't accept --profile after the script name · ae53e689
    Philip Chimento authored
    This was only intended for arguments that were previously accepted after
    the script name on the command line. For example, in some previous
    versions,
    
        gjs myapp.js -C myfiles
    
    would pass the -C argument to GJS and hide it from myapp.js. We changed
    this some time ago, but kept the behaviour for these arguments while
    logging a warning.
    
    However, new arguments should not get this behaviour. I accidentally
    implemented it for --profile. Technically it's not backwards compatible
    to remove it now, but I think it's OK because nobody would ever have
    been able to write
    
        gjs myapp.js --profile myapp.syscap
    
    without getting a warning stating that they shouldn't do that.
    ae53e689
  • Philip Chimento's avatar
    release: Prepare for 1.56.2 · 51ebacc5
    Philip Chimento authored
    51ebacc5
Version 1.56.2
--------------
- Closed bugs and merge requests:
* Crash in BoxedInstance when struct could not be allocated directly [#240,
!285, Philip Chimento]
* Cairo conversion bugs [!286, Philip Chimento]
* Gjs crashes when binding inherited property to js added gobject-property
[#246, !289, Marco Trevisan]
* console: Don't accept --profile after the script name [!287, Philip
Chimento]
Version 1.56.1
--------------
- Closed bugs and merge requests:
* Calling dumpHeap() on non-existent directory causes crash [#134, !277,
Philip Chimento]
* Using Gio.MemoryInputStream.new_from_data ("string") causes segfault [#221,
!278, Philip Chimento]
* Fix gjs_context_eval() for non-zero-terminated strings [!281, Philip
Chimento]
Version 1.56.0
--------------
......
......@@ -3,7 +3,7 @@
m4_define(pkg_major_version, 1)
m4_define(pkg_minor_version, 56)
m4_define(pkg_micro_version, 1)
m4_define(pkg_micro_version, 2)
m4_define(pkg_version, pkg_major_version.pkg_minor_version.pkg_micro_version)
m4_define(pkg_int_version, (pkg_major_version * 100 + pkg_minor_version) * 100 + pkg_micro_version)
......
......@@ -1222,11 +1222,8 @@ gjs_g_array_new_for_type(JSContext *context,
return g_array_sized_new(true, false, element_size, length);
}
GJS_USE
static gchar *
get_argument_display_name(const char *arg_name,
GjsArgumentType arg_type)
{
char* gjs_argument_display_name(const char* arg_name,
GjsArgumentType arg_type) {
switch (arg_type) {
case GJS_ARGUMENT_ARGUMENT:
return g_strdup_printf("Argument '%s'", arg_name);
......@@ -1274,12 +1271,11 @@ throw_invalid_argument(JSContext *context,
const char *arg_name,
GjsArgumentType arg_type)
{
gchar *display_name = get_argument_display_name(arg_name, arg_type);
GjsAutoChar display_name = gjs_argument_display_name(arg_name, arg_type);
gjs_throw(context, "Expected type %s for %s but got type '%s'",
type_tag_to_human_string(arginfo),
display_name, JS::InformalValueTypeName(value));
g_free(display_name);
type_tag_to_human_string(arginfo), display_name.get(),
JS::InformalValueTypeName(value));
}
GJS_JSAPI_RETURN_CONVENTION
......@@ -2066,21 +2062,18 @@ _Pragma("GCC diagnostic pop")
}
return false;
} else if (G_UNLIKELY(out_of_range)) {
gchar *display_name = get_argument_display_name (arg_name, arg_type);
GjsAutoChar display_name =
gjs_argument_display_name(arg_name, arg_type);
gjs_throw(context, "value is out of range for %s (type %s)",
display_name,
g_type_tag_to_string(type_tag));
g_free (display_name);
display_name.get(), g_type_tag_to_string(type_tag));
return false;
} else if (nullable_type &&
arg->v_pointer == NULL &&
!may_be_null) {
gchar *display_name = get_argument_display_name (arg_name, arg_type);
gjs_throw(context,
"%s (type %s) may not be null",
display_name,
GjsAutoChar display_name =
gjs_argument_display_name(arg_name, arg_type);
gjs_throw(context, "%s (type %s) may not be null", display_name.get(),
g_type_tag_to_string(type_tag));
g_free (display_name);
return false;
} else {
return true;
......
......@@ -34,7 +34,8 @@
G_BEGIN_DECLS
/* Different roles for a GArgument */
// Different roles for a GIArgument; currently used only in exception and debug
// messages.
typedef enum {
GJS_ARGUMENT_ARGUMENT,
GJS_ARGUMENT_RETURN_VALUE,
......@@ -44,6 +45,9 @@ typedef enum {
GJS_ARGUMENT_ARRAY_ELEMENT
} GjsArgumentType;
GJS_USE
char* gjs_argument_display_name(const char* arg_name, GjsArgumentType arg_type);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_value_to_arg(JSContext *context,
JS::HandleValue value,
......
......@@ -44,7 +44,8 @@
#include <girepository.h>
BoxedInstance::BoxedInstance(JSContext* cx, JS::HandleObject obj)
: GIWrapperInstance(cx, obj) {
: GIWrapperInstance(cx, obj), m_owning_ptr(false) {
m_ptr = nullptr;
GJS_INC_COUNTER(boxed_instance);
}
......@@ -113,7 +114,7 @@ BoxedBase* BoxedBase::get_copy_source(JSContext* context,
void BoxedInstance::allocate_directly(void) {
g_assert(get_prototype()->can_allocate_directly());
m_ptr = g_slice_alloc0(g_struct_info_get_size(info()));
own_ptr(g_slice_alloc0(g_struct_info_get_size(info())));
m_allocated_directly = true;
debug_lifecycle("Boxed pointer directly allocated");
......@@ -254,7 +255,7 @@ static bool boxed_invoke_constructor(JSContext* context, JS::HandleObject obj,
* pointer or another BoxedInstance.
*/
void BoxedInstance::copy_boxed(void* boxed_ptr) {
m_ptr = g_boxed_copy(gtype(), boxed_ptr);
own_ptr(g_boxed_copy(gtype(), boxed_ptr));
debug_lifecycle("Boxed pointer created with g_boxed_copy()");
}
......@@ -307,7 +308,6 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
// The return value of GLib.Variant.new_internal() gets its own
// BoxedInstance, and the one we're setting up in this constructor is
// discarded.
m_not_owning_ptr = true;
debug_lifecycle(
"Boxed construction delegated to GVariant constructor, "
"boxed object discarded");
......@@ -338,7 +338,7 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
return false;
}
m_ptr = rval_arg.v_pointer;
own_ptr(rval_arg.v_pointer);
debug_lifecycle("Boxed pointer created from zero-args constructor");
......@@ -361,9 +361,7 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
}
// The return value of the JS constructor gets its own BoxedInstance,
// and this one is discarded. Mark that the boxed pointer doesn't need
// to be freed, since it remains null.
m_not_owning_ptr = true;
// and this one is discarded.
debug_lifecycle(
"Boxed construction delegated to JS constructor, "
"boxed object discarded");
......@@ -393,7 +391,7 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
}
BoxedInstance::~BoxedInstance() {
if (!m_not_owning_ptr) {
if (m_owning_ptr) {
if (m_allocated_directly) {
g_slice_free1(g_struct_info_get_size(info()), m_ptr);
} else {
......@@ -476,8 +474,7 @@ bool BoxedInstance::get_nested_interface_object(
BoxedInstance* priv = BoxedInstance::new_for_js_object(context, obj);
/* A structure nested inside a parent object; doesn't have an independent allocation */
priv->m_ptr = raw_ptr() + offset;
priv->m_not_owning_ptr = true;
priv->share_ptr(raw_ptr() + offset);
priv->debug_lifecycle(
"Boxed pointer created, pointing inside memory owned by parent");
......@@ -1011,8 +1008,7 @@ JSObject* gjs_boxed_from_c_struct(JSContext* cx, GIStructInfo* info,
bool BoxedInstance::init_from_c_struct(JSContext* cx, void* gboxed, NoCopy) {
// We need to create a JS Boxed which references the original C struct, not
// a copy of it. Used for G_SIGNAL_TYPE_STATIC_SCOPE.
m_ptr = gboxed;
m_not_owning_ptr = true;
share_ptr(gboxed);
debug_lifecycle("Boxed pointer acquired, memory not owned");
return true;
}
......@@ -1022,7 +1018,7 @@ bool BoxedInstance::init_from_c_struct(JSContext* cx, void* gboxed) {
copy_boxed(gboxed);
return true;
} else if (gtype() == G_TYPE_VARIANT) {
m_ptr = g_variant_ref_sink(static_cast<GVariant*>(gboxed));
own_ptr(g_variant_ref_sink(static_cast<GVariant*>(gboxed)));
debug_lifecycle("Boxed pointer created by sinking GVariant ref");
return true;
} else if (get_prototype()->can_allocate_directly()) {
......
......@@ -161,12 +161,25 @@ class BoxedInstance
friend class BoxedBase; // for field_getter, etc.
bool m_allocated_directly : 1;
bool m_not_owning_ptr : 1; // if set, the JS wrapper does not own the C
// memory referred to by m_ptr.
bool m_owning_ptr : 1; // if set, the JS wrapper owns the C memory referred
// to by m_ptr.
explicit BoxedInstance(JSContext* cx, JS::HandleObject obj);
~BoxedInstance(void);
// Don't set GIWrapperBase::m_ptr directly. Instead, use one of these
// setters to express your intention to own the pointer or not.
void own_ptr(void* boxed_ptr) {
g_assert(!m_ptr);
m_ptr = boxed_ptr;
m_owning_ptr = true;
}
void share_ptr(void* unowned_boxed_ptr) {
g_assert(!m_ptr);
m_ptr = unowned_boxed_ptr;
m_owning_ptr = false;
}
// Methods for different ways to allocate the GBoxed pointer
void allocate_directly(void);
......
......@@ -77,8 +77,7 @@ gjs_foreign_load_foreign_module(JSContext *context,
script = g_strdup_printf("imports.%s;", gi_namespace);
JS::RootedValue retval(context);
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
if (!gjs->eval_with_scope(nullptr, script, strlen(script), "<internal>",
&retval)) {
if (!gjs->eval_with_scope(nullptr, script, -1, "<internal>", &retval)) {
g_critical("ERROR importing foreign module %s\n", gi_namespace);
g_free(script);
return false;
......
......@@ -143,6 +143,8 @@ static void gjs_object_set_gproperty(GObject* object, unsigned property_id,
JSContext *cx = current_context();
JS::RootedObject js_obj(cx, priv->wrapper());
JSAutoCompartment ac(cx, js_obj);
if (!jsobj_set_gproperty(cx, js_obj, value, pspec))
gjs_log_exception(cx);
}
......
......@@ -42,7 +42,6 @@ static bool enable_profiler = false;
static gboolean parse_profile_arg(const char *, const char *, void *, GError **);
/* Keep in sync with entries in check_script_args_for_stray_gjs_args() */
// clang-format off
static GOptionEntry entries[] = {
{ "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Print GJS version and exit" },
......@@ -115,19 +114,6 @@ parse_profile_arg(const char *option_name,
return true;
}
static gboolean
check_stray_profile_arg(const char *option_name,
const char *value,
void *data,
GError **error_out)
{
g_warning("You used the --profile option after the script on the GJS "
"command line. Support for this will be removed in a future "
"version. Place the option before the script or use the "
"GJS_ENABLE_PROFILER environment variable.");
return parse_profile_arg(option_name, value, data, error_out);
}
static void
check_script_args_for_stray_gjs_args(int argc,
char * const *argv)
......@@ -136,13 +122,13 @@ check_script_args_for_stray_gjs_args(int argc,
char **new_coverage_prefixes = NULL;
char *new_coverage_output_path = NULL;
char **new_include_paths = NULL;
/* Keep in sync with entries[] at the top */
// Don't add new entries here. This is only for arguments that were
// previously accepted after the script name on the command line, for
// backwards compatibility.
static GOptionEntry script_check_entries[] = {
{ "coverage-prefix", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &new_coverage_prefixes },
{ "coverage-output", 0, 0, G_OPTION_ARG_STRING, &new_coverage_output_path },
{ "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &new_include_paths },
{ "profile", 0, G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME,
G_OPTION_ARG_CALLBACK, reinterpret_cast<void *>(&check_stray_profile_arg) },
{ NULL }
};
char **argv_copy = g_new(char *, argc + 2);
......
......@@ -931,7 +931,7 @@ gjs_context_eval_file(GjsContext *js_context,
* GjsContextPrivate::eval_with_scope:
* @scope_object: an object to use as the global scope, or nullptr
* @script: JavaScript program encoded in UTF-8
* @script_len: length of @script
* @script_len: length of @script, or -1 if @script is 0-terminated
* @filename: filename to use as the origin of @script
* @retval: location for the return value of @script
*
......@@ -945,14 +945,7 @@ bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
const char* script, ssize_t script_len,
const char* filename,
JS::MutableHandleValue retval) {
int start_line_number = 1;
JSAutoRequest ar(m_cx);
size_t real_len = script_len;
if (script_len < 0)
real_len = strlen(script);
script = gjs_strip_unix_shebang(script, &real_len, &start_line_number);
/* log and clear exception if it's set (should not be, normally...) */
if (JS_IsExceptionPending(m_cx)) {
......@@ -964,18 +957,13 @@ bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
if (!eval_obj)
eval_obj = JS_NewPlainObject(m_cx);
JS::CompileOptions options(m_cx);
options.setFileAndLine(filename, start_line_number).setSourceIsLazy(true);
std::u16string utf16_string = gjs_utf8_script_to_utf16(script, script_len);
#if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
std::wstring wscript = gjs_win32_vc140_utf8_to_utf16(script);
std::u16string utf16_string(reinterpret_cast<const char16_t*>(wscript.c_str()));
#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
std::u16string utf16_string = convert.from_bytes(script);
#endif
unsigned start_line_number = 1;
size_t offset = gjs_unix_shebang_len(utf16_string, &start_line_number);
JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(),
JS::SourceBufferHolder buf(utf16_string.c_str() + offset,
utf16_string.size() - offset,
JS::SourceBufferHolder::NoOwnership);
JS::AutoObjectVector scope_chain(m_cx);
......@@ -984,6 +972,9 @@ bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
return false;
}
JS::CompileOptions options(m_cx);
options.setFileAndLine(filename, start_line_number);
if (!JS::Evaluate(m_cx, scope_chain, options, buf, retval))
return false;
......
......@@ -613,57 +613,35 @@ gjs_maybe_gc (JSContext *context)
}
/**
* gjs_strip_unix_shebang:
* gjs_unix_shebang_len:
*
* @script: (in): A pointer to a JS script
* @script_len: (inout): A pointer to the script length. The
* pointer will be modified if a shebang is stripped.
* @new_start_line_number: (out) (allow-none): A pointer to
* write the start-line number to account for the offset
* as a result of stripping the shebang.
* @script: A JS script
* @start_line_number: (out): the new start-line number to account for the
* offset as a result of stripping the shebang; can be either 1 or 2.
*
* Returns a pointer to the beginning of a script with unix
* shebangs removed. The outparams are useful to know the
* new length of the script and on what line of the
* Returns the offset in @script where the actual script begins with Unix
* shebangs removed. The outparam is useful to know what line of the
* original script we're executing from, so that any relevant
* offsets can be applied to the results of an execution pass.
*/
const char *
gjs_strip_unix_shebang(const char *script,
size_t *script_len,
int *start_line_number_out)
{
g_assert(script_len);
/* handle scripts with UNIX shebangs */
if (strncmp(script, "#!", 2) == 0) {
/* If we found a newline, advance the script by one line */
const char *s = (const char *) strstr (script, "\n");
if (s != NULL) {
if (*script_len > 0)
*script_len -= (s + 1 - script);
script = s + 1;
if (start_line_number_out)
*start_line_number_out = 2;
return script;
} else {
/* Just a shebang */
if (start_line_number_out)
*start_line_number_out = -1;
*script_len = 0;
return NULL;
}
size_t gjs_unix_shebang_len(const std::u16string& script,
unsigned* start_line_number) {
g_assert(start_line_number);
if (script.compare(0, 2, u"#!") != 0) {
// No shebang, leave the script unchanged
*start_line_number = 1;
return 0;
}
/* No shebang, return the original script */
if (start_line_number_out)
*start_line_number_out = 1;
*start_line_number = 2;
size_t newline_pos = script.find('\n', 2);
if (newline_pos == std::u16string::npos)
return script.size(); // Script consists only of a shebang line
return script;
// Point the offset after the newline
return newline_pos + 1;
}
#if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
......@@ -674,17 +652,30 @@ gjs_strip_unix_shebang(const char *script,
* obtain from MultiByteToWideChar(). See:
* https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
*/
std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str) {
int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
if (len == 0)
static std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str,
ssize_t len) {
int bufsize = MultiByteToWideChar(CP_UTF8, 0, str, len, nullptr, 0);
if (bufsize == 0)
return nullptr;
std::wstring wstr(len, 0);
int result = MultiByteToWideChar(CP_UTF8, 0, str, -1, &wstr[0], len);
std::wstring wstr(bufsize, 0);
int result = MultiByteToWideChar(CP_UTF8, 0, str, len, &wstr[0], bufsize);
if (result == 0)
return nullptr;
wstr.resize(strlen(str));
wstr.resize(len < 0 ? strlen(str) : len);
return wstr;
}
#endif
std::u16string gjs_utf8_script_to_utf16(const char* script, ssize_t len) {
#if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
std::wstring wscript = gjs_win32_vc140_utf8_to_utf16(script, len);
return std::u16string(reinterpret_cast<const char16_t*>(wscript.c_str()));
#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
if (len < 0)
return convert.from_bytes(script);
return convert.from_bytes(script, script + len);
#endif
}
......@@ -299,13 +299,14 @@ bool gjs_unichar_from_string (JSContext *context,
void gjs_maybe_gc (JSContext *context);
void gjs_gc_if_needed(JSContext *cx);
GJS_USE
const char * gjs_strip_unix_shebang(const char *script,
size_t *script_len,
int *new_start_line_number);
G_END_DECLS
GJS_USE
size_t gjs_unix_shebang_len(const std::u16string& script,
unsigned* start_line_number);
GJS_USE
std::u16string gjs_utf8_script_to_utf16(const char* script, ssize_t len);
GJS_JSAPI_RETURN_CONVENTION
GjsAutoChar gjs_format_stack_trace(JSContext *cx,
JS::HandleObject saved_frame);
......
......@@ -88,27 +88,17 @@ class GjsModule {
/* Carries out the actual execution of the module code */
GJS_JSAPI_RETURN_CONVENTION
bool
evaluate_import(JSContext *cx,
JS::HandleObject module,
const char *script,
size_t script_len,
const char *filename,
int line_number)
{
JS::CompileOptions options(cx);
options.setFileAndLine(filename, line_number);
#if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
std::wstring wscript = gjs_win32_vc140_utf8_to_utf16(script);
std::u16string utf16_string(
reinterpret_cast<const char16_t*>(wscript.c_str()));
#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
std::u16string utf16_string = convert.from_bytes(script);
#endif
JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(),
bool evaluate_import(JSContext* cx, JS::HandleObject module,
const char* script, ssize_t script_len,
const char* filename) {
std::u16string utf16_string =
gjs_utf8_script_to_utf16(script, script_len);
unsigned start_line_number = 1;
size_t offset = gjs_unix_shebang_len(utf16_string, &start_line_number);
JS::SourceBufferHolder buf(utf16_string.c_str() + offset,
utf16_string.size() - offset,
JS::SourceBufferHolder::NoOwnership);
JS::AutoObjectVector scope_chain(cx);
......@@ -117,6 +107,9 @@ class GjsModule {
return false;
}
JS::CompileOptions options(cx);
options.setFileAndLine(filename, start_line_number);
JS::RootedValue ignored_retval(cx);
if (!JS::Evaluate(cx, scope_chain, options, buf, &ignored_retval))
return false;
......@@ -139,7 +132,6 @@ class GjsModule {
GError *error = nullptr;
char *unowned_script;
size_t script_len = 0;
int start_line_number = 1;
if (!(g_file_load_contents(file, nullptr, &unowned_script, &script_len,
nullptr, &error)))
......@@ -148,12 +140,8 @@ class GjsModule {
GjsAutoChar script = unowned_script; /* steals ownership */
g_assert(script != nullptr);
const char *stripped_script =
gjs_strip_unix_shebang(script, &script_len, &start_line_number);
GjsAutoChar full_path = g_file_get_parse_name(file);
return evaluate_import(cx, module, stripped_script, script_len,
full_path, start_line_number);
return evaluate_import(cx, module, script, script_len, full_path);
}
/* JSClass operations */
......
......@@ -155,9 +155,6 @@ report "--coverage-prefix after script should succeed but give a warning"
$gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Gjs-WARNING.*--coverage-output'
report "--coverage-output after script should succeed but give a warning"
rm -f foo/coverage.lcov
$gjs -c 'imports.system.exit(0)' --profile=foo 2>&1 | grep -q 'Gjs-WARNING.*--profile'
report "--profile after script should succeed but give a warning"
rm -rf foo
for version_arg in --version --jsversion; do
# --version and --jsversion work
......
......@@ -976,7 +976,19 @@ context_to_g_argument(JSContext *context,
bool may_be_null,
GArgument *arg)
{
JS::RootedObject obj(context, value.toObjectOrNull());
if (value.isNull()) {
if (!may_be_null) {
GjsAutoChar display_name =
gjs_argument_display_name(arg_name, argument_type);
gjs_throw(context, "%s may not be null", display_name.get());
return false;
}
arg->v_pointer = nullptr;
return true;
}
JS::RootedObject obj(context, &value.toObject());
cairo_t *cr;
cr = gjs_cairo_context_get_context(context, obj);
......@@ -1012,7 +1024,8 @@ context_release_argument(JSContext *context,
GITransfer transfer,
GArgument *arg)
{
cairo_destroy((cairo_t*)arg->v_pointer);
if (transfer != GI_TRANSFER_NOTHING)
cairo_destroy(static_cast<cairo_t*>(arg->v_pointer));
return true;
}
......
......@@ -314,6 +314,18 @@ region_to_g_argument(JSContext *context,
bool may_be_null,
GArgument *arg)
{
if (value.isNull()) {
if (!may_be_null) {
GjsAutoChar display_name =
gjs_argument_display_name(arg_name, argument_type);
gjs_throw(context, "%s may not be null", display_name.get());
return false;
}
arg->v_pointer = nullptr;
return true;
}
JS::RootedObject obj(context, &value.toObject());
cairo_region_t *region;
......@@ -348,7 +360,8 @@ region_release_argument(JSContext *context,
GITransfer transfer,
GArgument *arg)
{
cairo_region_destroy((cairo_region_t*)arg->v_pointer);
if (transfer != GI_TRANSFER_NOTHING)
cairo_region_destroy(static_cast<cairo_region_t*>(arg->v_pointer));
return true;
}
......
......@@ -255,6 +255,18 @@ surface_to_g_argument(JSContext *context,
bool may_be_null,
GArgument *arg)
{
if (value.isNull()) {
if (!may_be_null) {
GjsAutoChar display_name =
gjs_argument_display_name(arg_name, argument_type);
gjs_throw(context, "%s may not be null", display_name.get());
return false;
}
arg->v_pointer = nullptr;
return true;
}
JSObject *obj;
cairo_surface_t *s;
......@@ -290,7 +302,8 @@ surface_release_argument(JSContext *context,
GITransfer transfer,
GArgument *arg)
{
cairo_surface_destroy((cairo_surface_t*)arg->v_pointer);
if (transfer != GI_TRANSFER_NOTHING)
cairo_surface_destroy(static_cast<cairo_surface_t*>(arg->v_pointer));
return true;
}
......
......@@ -65,6 +65,19 @@ gjstest_test_func_gjs_context_construct_eval(void)
g_object_unref (context);
}
static void gjstest_test_func_gjs_context_eval_non_zero_terminated(void) {
GjsAutoUnref<GjsContext> gjs = gjs_context_new();
GError* error = NULL;
int status;
// This string is invalid JS if it is treated as zero-terminated
bool ok = gjs_context_eval(gjs, "77!", 2, "<input>", &status, &error);
g_assert_true(ok);
g_assert_no_error(error);
g_assert_cmpint(status, ==, 77);
}
static void
gjstest_test_func_gjs_context_exit(void)
{
......@@ -329,52 +342,37 @@ gjstest_test_func_util_glib_strv_concat_pointers(void)
static void
gjstest_test_strip_shebang_no_advance_for_no_shebang(void)
{
const char *script = "foo\nbar";
size_t script_len_original = strlen(script);
size_t script_len = script_len_original;
int line_number = 1;
const char *stripped = gjs_strip_unix_shebang(script,
&script_len,
&line_number);
g_assert_cmpstr(script, ==, stripped);
g_assert(script_len == script_len_original);
g_assert(line_number == 1);
unsigned line_number = 1;
size_t offset = gjs_unix_shebang_len(u"foo\nbar", &line_number);
g_assert_cmpuint(offset, ==, 0);
g_assert_cmpuint(line_number, ==, 1);
}
static void gjstest_test_strip_shebang_no_advance_for_too_short_string(void) {
unsigned line_number = 1;
size_t offset = gjs_unix_shebang_len(u"Z", &line_number);
g_assert_cmpuint(offset, ==, 0);
g_assert_cmpuint(line_number, ==, 1);
}
static void
gjstest_test_strip_shebang_advance_for_shebang(void)
{
const char *script = "#!foo\nbar";
size_t script_len_original = strlen(script);
size_t script_len = script_len_original;
int line_number = 1;
const char *stripped = gjs_strip_unix_shebang(script,
&script_len,
&line_number);
g_assert_cmpstr(stripped, ==, "bar");
g_assert(script_len == 3);
g_assert(line_number == 2);
unsigned line_number = 1;
size_t offset = gjs_unix_shebang_len(u"#!foo\nbar", &line_number);
g_assert_cmpuint(offset, ==, 6);
g_assert_cmpuint(line_number, ==, 2);
}
static void
gjstest_test_strip_shebang_return_null_for_just_shebang(void)
{
const char *script = "#!foo";
size_t script_len_original = strlen(script);
size_t script_len = script_len_original;
int line_number = 1;
const char *stripped = gjs_strip_unix_shebang(script,
&script_len,
&line_number);
g_assert(stripped == NULL);
g_assert(script_len == 0);
g_assert(line_number == -1);
static void gjstest_test_strip_shebang_advance_to_end_for_just_shebang(void) {
unsigned line_number = 1;
size_t offset = gjs_unix_shebang_len(u"#!foo", &line_number);
g_assert_cmpuint(offset, ==, 5);
g_assert_cmpuint(line_number, ==, 2);
}
static void
......@@ -414,11 +412,16 @@ main(int argc,
g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy);
g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval);
g_test_add_func("/gjs/context/eval/non-zero-terminated",
gjstest_test_func_gjs_context_eval_non_zero_terminated);
g_test_add_func("/gjs/context/exit", gjstest_test_func_gjs_context_exit);
g_test_add_func("/gjs/gobject/js_defined_type", gjstest_test_func_gjs_gobject_js_defined_type);
g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang", gjstest_test_strip_shebang_no_advance_for_no_shebang);
g_test_add_func("/gjs/jsutil/strip_shebang/short_string",
gjstest_test_strip_shebang_no_advance_for_too_short_string);
g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang", gjstest_test_strip_shebang_advance_for_shebang);
g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang", gjstest_test_strip_shebang_return_null_for_just_shebang);
g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang",
gjstest_test_strip_shebang_advance_to_end_for_just_shebang);
g_test_add_func("/gjs/profiler/start_stop", gjstest_test_profiler_start_stop);
g_test_add_func("/util/glib/strv/concat/null", gjstest_test_func_util_glib_strv_concat_null);
g_test_add_func("/util/glib/strv/concat/pointers", gjstest_test_func_util_glib_strv_concat_pointers);
......