diff --git a/Make.inc b/Make.inc index db89e34b2cde5..9be382c5424bb 100644 --- a/Make.inc +++ b/Make.inc @@ -679,7 +679,7 @@ endif ifeq ($(OS), WINNT) ifneq ($(USEMSVC), 1) OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(JULIAHOME)/src/julia.expmap \ - $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -lssp + $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp JLDFLAGS = -Wl,--stack,8388608 ifeq ($(ARCH),i686) JLDFLAGS += -Wl,--large-address-aware diff --git a/Makefile b/Makefile index 851b05349b40b..83a972d8036c9 100644 --- a/Makefile +++ b/Makefile @@ -140,14 +140,14 @@ endif $(build_private_libdir)/sys0.o: @$(call PRINT_JULIA, cd base && \ - $(call spawn,$(JULIA_EXECUTABLE)) --build $(call cygpath_w,$(build_private_libdir)/sys0) sysimg.jl) + $(call spawn,$(JULIA_EXECUTABLE)) -C $(JULIA_CPU_TARGET) --build $(call cygpath_w,$(build_private_libdir)/sys0) sysimg.jl) BASE_SRCS := $(wildcard base/*.jl base/*/*.jl base/*/*/*.jl) ,:=, $(build_private_libdir)/sys.o: VERSION $(BASE_SRCS) $(build_docdir)/helpdb.jl $(build_private_libdir)/sys0.$(SHLIB_EXT) @$(call PRINT_JULIA, cd base && \ - $(call spawn,$(JULIA_EXECUTABLE)) --build $(call cygpath_w,$(build_private_libdir)/sys) \ + $(call spawn,$(JULIA_EXECUTABLE)) -C $(JULIA_CPU_TARGET) --build $(call cygpath_w,$(build_private_libdir)/sys) \ -J$(call cygpath_w,$(build_private_libdir))/$$([ -e $(build_private_libdir)/sys.ji ] && echo sys.ji || echo sys0.ji) -f sysimg.jl \ || { echo '*** This error is usually fixed by running `make clean`. If the error persists$(,) try `make cleanall`. ***' && false; } ) @@ -271,6 +271,8 @@ endif # Copy system image $(INSTALL_F) $(build_private_libdir)/sys.ji $(DESTDIR)$(private_libdir) $(INSTALL_M) $(build_private_libdir)/sys.$(SHLIB_EXT) $(DESTDIR)$(private_libdir) + # Copy in system image build script + $(INSTALL_M) contrib/build_sysimg.jl $(DESTDIR)$(datarootdir)/julia/ # Copy in all .jl sources as well cp -R -L $(build_datarootdir)/julia $(DESTDIR)$(datarootdir)/ # Copy documentation @@ -306,8 +308,8 @@ else ifeq ($(OS), Linux) done endif - # Overwrite JL_SYSTEM_IMAGE_PATH in julia binaries - for julia in $(DESTDIR)$(bindir)/julia* ; do \ + # Overwrite JL_SYSTEM_IMAGE_PATH in julia library + for julia in $(DESTDIR)$(private_libdir)/libjulia*.$(SHLIB_EXT) ; do \ $(call spawn,$(build_bindir)/stringreplace $$(strings -t x - $$julia | grep "sys.ji$$" | awk '{print $$1;}' ) "$(private_libdir_rel)/sys.ji" 256 $(call cygpath_w,$$julia)); \ done endif diff --git a/base/LineEdit.jl b/base/LineEdit.jl index ae777dcf86fb4..88972b7f00a0c 100644 --- a/base/LineEdit.jl +++ b/base/LineEdit.jl @@ -8,6 +8,7 @@ import ..Terminals: raw!, width, height, cmove, getX, import Base: ensureroom, peek, show, AnyDict abstract TextInterface +abstract ModeState export run_interface, Prompt, ModalInterface, transition, reset_state, edit_insert, keymap @@ -26,9 +27,6 @@ type MIState end MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0) -type Mode <: TextInterface -end - type Prompt <: TextInterface prompt first_prompt @@ -37,7 +35,7 @@ type Prompt <: TextInterface prompt_prefix # Same as prefix except after the prompt prompt_suffix - keymap_func + keymap_dict keymap_func_data complete on_enter @@ -53,7 +51,7 @@ immutable InputAreaState curs_row::Int64 end -type PromptState +type PromptState <: ModeState terminal::TextTerminal p::Prompt input_buffer::IOBuffer @@ -61,7 +59,7 @@ type PromptState indent::Int end -input_string(s::PromptState) = bytestring(pointer(s.input_buffer.data), s.input_buffer.size) +input_string(s::PromptState) = bytestring(s.input_buffer) input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) function input_string_newlines_aftercursor(s::PromptState) @@ -185,9 +183,10 @@ end prompt_string(s::PromptState) = s.p.prompt prompt_string(s::AbstractString) = s -refresh_multi_line(termbuf::TerminalBuffer, s::PromptState) = s.ias = +refresh_multi_line(s::ModeState) = refresh_multi_line(terminal(s), s) +refresh_multi_line(termbuf::TerminalBuffer, s::ModeState) = s.ias = refresh_multi_line(termbuf, terminal(s), buffer(s), s.ias, s, indent = s.indent) - +refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState) = (@assert term == terminal(s); refresh_multi_line(termbuf,s)) function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf, state::InputAreaState, prompt = ""; indent = 0) cols = width(terminal) @@ -299,6 +298,16 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf return InputAreaState(cur_row, curs_row) end +function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) + outbuf = IOBuffer() + termbuf = TerminalBuffer(outbuf) + ret = refresh_multi_line(termbuf, terminal, args...;kwargs...) + # Output the entire refresh at once + write(terminal, takebuf_array(outbuf)) + flush(terminal) + return ret +end + # Edit functionality is_non_word_char(c) = c in " \t\n\"\\'`@\$><=:;|&{}()[].,+-*/?%^~" @@ -609,8 +618,8 @@ history_next(::EmptyHistoryProvider) = ("", false) history_search(::EmptyHistoryProvider, args...) = false add_history(::EmptyHistoryProvider, s) = nothing add_history(s::PromptState) = add_history(mode(s).hist, s) -history_next_prefix(s, hist) = false -history_prev_prefix(s, hist) = false +history_next_prefix(s, hist, prefix) = false +history_prev_prefix(s, hist, prefix) = false function history_prev(s, hist) l, ok = history_prev(mode(s).hist) @@ -639,12 +648,12 @@ refresh_line(s, termbuf) = refresh_multi_line(termbuf, s) default_completion_cb(::IOBuffer) = [] default_enter_cb(_) = true -write_prompt(terminal, s::PromptState) = write_prompt(terminal, s, s.p.prompt) -function write_prompt(terminal, s::PromptState, prompt) - prefix = isa(s.p.prompt_prefix,Function) ? s.p.prompt_prefix() : s.p.prompt_prefix - suffix = isa(s.p.prompt_suffix,Function) ? s.p.prompt_suffix() : s.p.prompt_suffix +write_prompt(terminal, s::PromptState) = write_prompt(terminal, s.p) +function write_prompt(terminal, p::Prompt) + prefix = isa(p.prompt_prefix,Function) ? p.prompt_prefix() : p.prompt_prefix + suffix = isa(p.prompt_suffix,Function) ? p.prompt_suffix() : p.prompt_suffix write(terminal, prefix) - write(terminal, prompt) + write(terminal, p.prompt) write(terminal, Base.text_colors[:normal]) write(terminal, suffix) end @@ -725,18 +734,21 @@ function normalize_keymap(keymap::Dict) ret end -match_input(k::Function, s, cs) = (update_key_repeats(s, cs); return keymap_fcn(k, s, last(cs))) -match_input(k::Void, s, cs) = (s,p) -> return :ok -function match_input(keymap::Dict, s, cs=Char[]) - c = read(terminal(s), Char) +match_input(k::Function, s, term, cs) = (update_key_repeats(s, cs); return keymap_fcn(k, s, ByteString(cs))) +match_input(k::Void, s, term, cs) = (s,p) -> return :ok +function match_input(keymap::Dict, s, term=terminal(s), cs=Char[]) + # if we run out of characters to match before resolving an action, + # return an empty keymap function + eof(term) && return keymap_fcn(nothing, s, "") + c = read(term, Char) push!(cs, c) k = haskey(keymap, c) ? c : '\0' # if we don't match on the key, look for a default action then fallback on 'nothing' to ignore - return match_input(get(keymap, k, nothing), s, cs) + return match_input(get(keymap, k, nothing), s, term, cs) end keymap_fcn(f::Void, s, c) = (s, p) -> return :ok -function keymap_fcn(f::Function, s, c::Char) +function keymap_fcn(f::Function, s, c) return (s, p) -> begin r = f(s, p, c) if isa(r, Symbol) @@ -829,8 +841,7 @@ end function keymap{D<:Dict}(keymaps::Array{D}) # keymaps is a vector of prioritized keymaps, with highest priority first dict = map(normalize_keys, keymaps) - dict = keymap_prepare(merge(reverse(dict)...)) - return (s,p)->match_input(dict, s)(s,p) + return keymap_prepare(merge(reverse(dict)...)) end const escape_defaults = merge!( @@ -868,7 +879,7 @@ function write_response_buffer(s::PromptState, data) refresh_line(s) end -type SearchState +type SearchState <: ModeState terminal histprompt #rsearch (true) or ssearch (false) @@ -898,35 +909,7 @@ function history_set_backward(s::SearchState, backward) s.backward = backward end -input_string(s::SearchState) = bytestring(pointer(s.query_buffer.data), s.query_buffer.size) - -refresh_multi_line(termbuf::TerminalBuffer, term, s::Union(SearchState,PromptState)) = (@assert term == terminal(s); refresh_multi_line(termbuf,s)) -function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) - buf = IOBuffer() - write(buf, pointer(s.query_buffer.data), s.query_buffer.ptr-1) - write(buf, "': ") - offset = buf.ptr - ptr = s.response_buffer.ptr - seek(s.response_buffer, 0) - write(buf, readall(s.response_buffer)) - buf.ptr = offset + ptr - 1 - s.response_buffer.ptr = ptr - s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") -end - -function refresh_multi_line(s::Union(SearchState,PromptState)) - refresh_multi_line(terminal(s), s) -end - -function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) - outbuf = IOBuffer() - termbuf = TerminalBuffer(outbuf) - ret = refresh_multi_line(termbuf, terminal, args...;kwargs...) - # Output the entire refresh at once - write(terminal, takebuf_array(outbuf)) - flush(terminal) - return ret -end +input_string(s::SearchState) = bytestring(s.query_buffer) function reset_state(s::SearchState) if s.query_buffer.size != 0 @@ -943,18 +926,84 @@ end type HistoryPrompt{T<:HistoryProvider} <: TextInterface hp::T complete - keymap_func::Function + keymap_dict::Dict{Char,Any} HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) end HistoryPrompt{T<:HistoryProvider}(hp::T) = HistoryPrompt{T}(hp) init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) +type PrefixSearchState <: ModeState + terminal + histprompt + prefix::ByteString + response_buffer::IOBuffer + ias::InputAreaState + indent::Int + #The prompt whose input will be replaced by the matched history + parent + PrefixSearchState(terminal, histprompt, prefix, response_buffer) = + new(terminal, histprompt, prefix, response_buffer, InputAreaState(0,0), 0) +end + +input_string(s::PrefixSearchState) = bytestring(s.response_buffer) + +# a meta-prompt that presents itself as parent_prompt, but which has an independent keymap +# for prefix searching +type PrefixHistoryPrompt{T<:HistoryProvider} <: TextInterface + hp::T + parent_prompt::Prompt + complete + keymap_dict::Dict{Char,Any} + PrefixHistoryPrompt(hp, parent_prompt) = new(hp, parent_prompt, EmptyCompletionProvider()) +end + +PrefixHistoryPrompt{T<:HistoryProvider}(hp::T, parent_prompt) = PrefixHistoryPrompt{T}(hp, parent_prompt) +init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) + +write_prompt(terminal, s::PrefixSearchState) = write_prompt(terminal, s.histprompt.parent_prompt) +prompt_string(s::PrefixSearchState) = s.histprompt.parent_prompt.prompt + +terminal(s::PrefixSearchState) = s.terminal + +function reset_state(s::PrefixSearchState) + if s.response_buffer.size != 0 + s.response_buffer.size = 0 + s.response_buffer.ptr = 1 + end +end + +function transition(s::PrefixSearchState, mode) + s.parent = mode + s.histprompt.parent_prompt = mode +end + +replace_line(s::PrefixSearchState, l::IOBuffer) = s.response_buffer = l +function replace_line(s::PrefixSearchState, l) + s.response_buffer.ptr = 1 + s.response_buffer.size = 0 + write(s.response_buffer, l) +end + +function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) + buf = IOBuffer() + write(buf, pointer(s.query_buffer.data), s.query_buffer.ptr-1) + write(buf, "': ") + offset = buf.ptr + ptr = s.response_buffer.ptr + seek(s.response_buffer, 0) + write(buf, readall(s.response_buffer)) + buf.ptr = offset + ptr - 1 + s.response_buffer.ptr = ptr + s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") +end + state(s::MIState, p) = s.mode_state[p] state(s::PromptState, p) = (@assert s.p == p; s) mode(s::MIState) = s.current_mode mode(s::PromptState) = s.p mode(s::SearchState) = @assert false +mode(s::PrefixSearchState) = s.histprompt.parent_prompt # Search Mode completions function complete_line(s::SearchState, repeats) @@ -995,6 +1044,22 @@ function enter_search(s::MIState, p::HistoryPrompt, backward::Bool) transition(s, p) end +function enter_prefix_search(s::MIState, p::PrefixHistoryPrompt, backward::Bool) + buf = buffer(s) + + pss = state(s, p) + pss.parent = mode(s) + pss.prefix = bytestring(pointer(buf.data), position(buf)) + copybuf!(pss.response_buffer, buf) + pss.indent = state(s, mode(s)).indent + transition(s, p) + if backward + history_prev_prefix(pss, pss.histprompt.hp, pss.prefix) + else + history_next_prefix(pss, pss.histprompt.hp, pss.prefix) + end +end + function setup_search_keymap(hp) p = HistoryPrompt(hp) pkeymap = AnyDict( @@ -1067,7 +1132,7 @@ function setup_search_keymap(hp) end, "*" => (s,data,c)->(edit_insert(data.query_buffer, c); update_display_buffer(s, data)) ) - p.keymap_func = keymap([pkeymap, escape_defaults]) + p.keymap_dict = keymap([pkeymap, escape_defaults]) skeymap = AnyDict( "^R" => (s,o...)->(enter_search(s, p, true)), "^S" => (s,o...)->(enter_search(s, p, false)), @@ -1075,8 +1140,8 @@ function setup_search_keymap(hp) (p, skeymap) end -keymap(state, p::HistoryPrompt) = p.keymap_func -keymap_data(state, ::HistoryPrompt) = state +keymap(state, p::Union(HistoryPrompt,PrefixHistoryPrompt)) = p.keymap_dict +keymap_data(state, ::Union(HistoryPrompt, PrefixHistoryPrompt)) = state Base.isempty(s::PromptState) = s.input_buffer.size == 0 @@ -1225,33 +1290,69 @@ AnyDict( end edit_insert(s, input) end, - "^T" => (s,o...)->edit_transpose(s), + "^T" => (s,o...)->edit_transpose(s) ) const history_keymap = AnyDict( "^P" => (s,o...)->(history_prev(s, mode(s).hist)), "^N" => (s,o...)->(history_next(s, mode(s).hist)), # Up Arrow - "\e[A" => (s,o...)->(edit_move_up(s) || history_prev_prefix(s, mode(s).hist)), + "\e[A" => (s,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), # Down Arrow - "\e[B" => (s,o...)->(edit_move_down(s) || history_next_prefix(s, mode(s).hist)), + "\e[B" => (s,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), # Page Up "\e[5~" => (s,o...)->(history_prev(s, mode(s).hist)), # Page Down "\e[6~" => (s,o...)->(history_next(s, mode(s).hist)) ) -function deactivate(p::Union(Prompt,HistoryPrompt), s::Union(SearchState,PromptState), termbuf) +const prefix_history_keymap = AnyDict( + # Up Arrow + "\e[A" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), + # Down Arrow + "\e[B" => (s,data,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), + # by default, pass thru to the parent mode + "*" => (s,data,c)->begin + accept_result(s, data.histprompt); + ps = state(s, mode(s)) + match_input(keymap(ps, mode(s)), s, IOBuffer(c))(s, keymap_data(ps, mode(s))) + end, + # match escape sequences for pass thru + "\e*" => "*", + "\e[*" => "*", + "\e[1~" => "*", + "\e[3~" => "*", + "\e[4~" => "*", + "\e[5~" => "*", + "\e[6~" => "*", + "\e[7~" => "*", + "\e[8~" => "*", + "\e[200~" => "*" +) + +function setup_prefix_keymap(hp, parent_prompt) + p = PrefixHistoryPrompt(hp, parent_prompt) + p.keymap_dict = keymap([prefix_history_keymap]) + pkeymap = AnyDict( + # Up Arrow + "\e[A" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), + # Down Arrow + "\e[B" => (s,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), + ) + (p, pkeymap) +end + +function deactivate(p::TextInterface, s::ModeState, termbuf) clear_input_area(termbuf, s) s end -function activate(p::Union(Prompt,HistoryPrompt), s::Union(SearchState,PromptState), termbuf) +function activate(p::TextInterface, s::ModeState, termbuf) s.ias = InputAreaState(0, 0) refresh_line(s, termbuf) end -function activate(p::Union(Prompt,HistoryPrompt), s::MIState, termbuf) +function activate(p::TextInterface, s::MIState, termbuf) @assert p == s.current_mode activate(p, s.mode_state[s.current_mode], termbuf) end @@ -1287,13 +1388,13 @@ function reset_state(s::MIState) end end -const default_keymap_func = keymap([default_keymap, escape_defaults]) +const default_keymap_dict = keymap([default_keymap, escape_defaults]) function Prompt(prompt; first_prompt = prompt, prompt_prefix = "", prompt_suffix = "", - keymap_func = default_keymap_func, + keymap_dict = default_keymap_dict, keymap_func_data = nothing, complete = EmptyCompletionProvider(), on_enter = default_enter_cb, @@ -1301,7 +1402,7 @@ function Prompt(prompt; hist = EmptyHistoryProvider(), sticky = false) - Prompt(prompt, first_prompt, prompt_prefix, prompt_suffix, keymap_func, keymap_func_data, + Prompt(prompt, first_prompt, prompt_prefix, prompt_suffix, keymap_dict, keymap_func_data, complete, on_enter, on_done, hist, sticky) end @@ -1332,30 +1433,31 @@ end buffer(s::PromptState) = s.input_buffer buffer(s::SearchState) = s.query_buffer +buffer(s::PrefixSearchState) = s.response_buffer -keymap(s::PromptState, prompt::Prompt) = prompt.keymap_func +keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode) keymap_data(ms::MIState, m::ModalInterface) = keymap_data(ms.mode_state[ms.current_mode], ms.current_mode) -function prompt!(terminal, prompt, s = init_state(terminal, prompt)) - Base.reseteof(terminal) - raw!(terminal, true) - enable_bracketed_paste(terminal) +function prompt!(term, prompt, s = init_state(term, prompt)) + Base.reseteof(term) + raw!(term, true) + enable_bracketed_paste(term) try - start_reading(terminal) - activate(prompt, s, terminal) + start_reading(term) + activate(prompt, s, term) while true - state = keymap(s, prompt)(s, keymap_data(s, prompt)) + state = match_input(keymap(s, prompt), s)(s, keymap_data(s, prompt)) if state == :abort - stop_reading(terminal) + stop_reading(term) return buffer(s), false, false elseif state == :done - stop_reading(terminal) + stop_reading(term) return buffer(s), true, false elseif state == :suspend @unix_only begin - stop_reading(terminal) + stop_reading(term) return buffer(s), true, true end else @@ -1363,7 +1465,7 @@ function prompt!(terminal, prompt, s = init_state(terminal, prompt)) end end finally - raw!(terminal, false) && disable_bracketed_paste(terminal) + raw!(term, false) && disable_bracketed_paste(term) end end diff --git a/base/REPL.jl b/base/REPL.jl index a0b5d57146813..e244d493c3b51 100644 --- a/base/REPL.jl +++ b/base/REPL.jl @@ -384,7 +384,7 @@ function add_history(hist::REPLHistoryProvider, s) flush(hist.history_file) end -function history_move(s::LineEdit.MIState, hist::REPLHistoryProvider, idx::Int, save_idx::Int = hist.cur_idx) +function history_move(s::Union(LineEdit.MIState,LineEdit.PrefixSearchState), hist::REPLHistoryProvider, idx::Int, save_idx::Int = hist.cur_idx) max_idx = length(hist.history) + 1 @assert 1 <= hist.cur_idx <= max_idx (1 <= idx <= max_idx) || return :none @@ -469,34 +469,39 @@ function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, end end -function history_move_prefix(s::LineEdit.MIState, +function history_move_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, + prefix::AbstractString, backwards::Bool) - buf = LineEdit.buffer(s) - pos = position(buf) - prefix = bytestring_beforecursor(buf) - allbuf = bytestring(buf) + cur_response = bytestring(LineEdit.buffer(s)) cur_idx = hist.cur_idx # when searching forward, start at last_idx if !backwards && hist.last_idx > 0 cur_idx = hist.last_idx end hist.last_idx = -1 - idxs = backwards ? ((cur_idx-1):-1:1) : ((cur_idx+1):length(hist.history)) + max_idx = length(hist.history)+1 + idxs = backwards ? ((cur_idx-1):-1:1) : ((cur_idx+1):max_idx) for idx in idxs - if beginswith(hist.history[idx], prefix) && hist.history[idx] != allbuf + if (idx == max_idx) || (beginswith(hist.history[idx], prefix) && (hist.history[idx] != cur_response || hist.modes[idx] != LineEdit.mode(s))) history_move(s, hist, idx) - seek(LineEdit.buffer(s), pos) + if length(prefix) == 0 + # on empty prefix search, move cursor to the end + LineEdit.move_input_end(s) + else + # otherwise, keep cursor at the prefix position as a visual cue + seek(LineEdit.buffer(s), length(prefix)) + end LineEdit.refresh_line(s) return :ok end end Terminals.beep(LineEdit.terminal(s)) end -history_next_prefix(s::LineEdit.MIState, hist::REPLHistoryProvider) = - history_move_prefix(s, hist, false) -history_prev_prefix(s::LineEdit.MIState, hist::REPLHistoryProvider) = - history_move_prefix(s, hist, true) +history_next_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = + history_move_prefix(s, hist, prefix, false) +history_prev_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = + history_move_prefix(s, hist, prefix, true) function history_search(hist::REPLHistoryProvider, query_buffer::IOBuffer, response_buffer::IOBuffer, backwards::Bool=false, skip_current::Bool=false) @@ -695,9 +700,8 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep shell_mode.hist = hp help_mode.hist = hp - hkp, hkeymap = LineEdit.setup_search_keymap(hp) - - hkp.complete = LatexCompletions() + search_prompt, skeymap = LineEdit.setup_search_keymap(hp) + search_prompt.complete = LatexCompletions() # Canonicalize user keymap input if isa(extra_repl_keymap, Dict) @@ -777,10 +781,12 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep end, ) - a = Dict{Any,Any}[hkeymap, repl_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] + prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, julia_prompt) + + a = Dict{Any,Any}[skeymap, repl_keymap, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(a, extra_repl_keymap) - julia_prompt.keymap_func = LineEdit.keymap(a) + julia_prompt.keymap_dict = LineEdit.keymap(a) const mode_keymap = AnyDict( '\b' => function (s,o...) @@ -803,12 +809,12 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep end ) - b = Dict{Any,Any}[hkeymap, mode_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] + b = Dict{Any,Any}[skeymap, mode_keymap, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(b, extra_repl_keymap) - shell_mode.keymap_func = help_mode.keymap_func = LineEdit.keymap(b) + shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) - ModalInterface([julia_prompt, shell_mode, help_mode,hkp]) + ModalInterface([julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt]) end function run_frontend(repl::LineEditREPL, backend) diff --git a/base/Terminals.jl b/base/Terminals.jl index d0a56c7cca152..9ac5db6ef1def 100644 --- a/base/Terminals.jl +++ b/base/Terminals.jl @@ -32,7 +32,8 @@ import Base: stop_reading, write, writemime, - reseteof + reseteof, + eof ## TextTerminal ## @@ -193,6 +194,7 @@ readuntil(t::UnixTerminal, s) = readuntil(t.in_stream, s) read(t::UnixTerminal, ::Type{UInt8}) = read(t.in_stream, UInt8) start_reading(t::UnixTerminal) = start_reading(t.in_stream) stop_reading(t::UnixTerminal) = stop_reading(t.in_stream) +eof(t::UnixTerminal) = eof(t.in_stream) @unix_only hascolor(t::TTYTerminal) = (beginswith(t.term_type, "xterm") || success(`tput setaf 0`)) @windows_only hascolor(t::TTYTerminal) = true diff --git a/base/boot.jl b/base/boot.jl index 0ecec7f2fa14c..8c6f984be0770 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -147,7 +147,7 @@ export # method reflection applicable, invoke, method_exists, # constants - JULIA_HOME, nothing, Main, + nothing, Main, # intrinsics module Intrinsics #ccall, cglobal, llvmcall, abs_float, add_float, add_int, and_int, ashr_int, diff --git a/base/client.jl b/base/client.jl index 3518498795e83..27ca40edba1e1 100644 --- a/base/client.jl +++ b/base/client.jl @@ -356,6 +356,7 @@ function load_machine_file(path::AbstractString) end function early_init() + global const JULIA_HOME = ccall(:jl_get_julia_home, Any, ()) Sys.init_sysinfo() if CPU_CORES > 8 && !("OPENBLAS_NUM_THREADS" in keys(ENV)) && !("OMP_NUM_THREADS" in keys(ENV)) # Prevent openblas from stating to many threads, unless/until specifically requested @@ -372,13 +373,10 @@ import .Terminals import .REPL function _start() - early_init() - try init_parallel() init_bind_addr(ARGS) any(a->(a=="--worker"), ARGS) || init_head_sched() - init_load_path() (quiet,repl,startup,color_set,no_history_file) = process_options(copy(ARGS)) local term @@ -412,32 +410,25 @@ function _start() # note: currently IOStream is used for file STDIN if isa(STDIN,File) || isa(STDIN,IOStream) # reading from a file, behave like include - eval(parse_input_line(readall(STDIN))) + eval(Main,parse_input_line(readall(STDIN))) else # otherwise behave repl-like while !eof(STDIN) eval_user_input(parse_input_line(STDIN), true) end end - if have_color - print(color_normal) - end - quit() + else + REPL.run_repl(active_repl) end - REPL.run_repl(active_repl) end catch err display_error(err,catch_backtrace()) println() exit(1) end - if is_interactive - if have_color - print(color_normal) - end - println() + if is_interactive && have_color + print(color_normal) end - ccall(:uv_atexit_hook, Void, ()) end const atexit_hooks = [] diff --git a/base/dSFMT.jl b/base/dSFMT.jl index a77791db58c90..274810dc27328 100644 --- a/base/dSFMT.jl +++ b/base/dSFMT.jl @@ -67,8 +67,8 @@ end ## Windows entropy @windows_only begin - function win32_SystemFunction036!(a::Array{UInt32}) - ccall((:SystemFunction036,:Advapi32),stdcall,UInt8,(Ptr{Void},UInt32),a,length(a)*sizeof(eltype(a))) + function win32_SystemFunction036!{T}(a::Array{T}) + ccall((:SystemFunction036,:Advapi32),stdcall,UInt8,(Ptr{Void},UInt32),a,sizeof(a)) end end diff --git a/base/exports.jl b/base/exports.jl index 07c846233877f..f9cad968ff2b0 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -163,6 +163,7 @@ export Inf, Inf16, Inf32, + JULIA_HOME, LOAD_PATH, MS_ASYNC, MS_INVALIDATE, @@ -901,6 +902,7 @@ export # random numbers AbstractRNG, MersenneTwister, + RandomDevice, rand!, rand, randbool, diff --git a/base/precompile.jl b/base/precompile.jl index 918bfd704bf47..277adf6669946 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -72,11 +72,11 @@ precompile(Base.LineEdit.init_state, (Base.Terminals.TTYTerminal, Base.LineEdit. precompile(Base.LineEdit.input_string, (Base.LineEdit.PromptState,)) precompile(Base.LineEdit.keymap, (Base.LineEdit.PromptState, Base.LineEdit.Prompt)) precompile(Base.LineEdit.keymap_data, (Base.LineEdit.PromptState, Base.LineEdit.Prompt)) -precompile(Base.LineEdit.keymap_fcn, (Function, Base.LineEdit.MIState, Char)) -precompile(Base.LineEdit.keymap_fcn, (Function, Base.LineEdit.MIState,Char)) +precompile(Base.LineEdit.keymap_fcn, (Function, Base.LineEdit.MIState, ByteString)) +precompile(Base.LineEdit.keymap_fcn, (Function, Base.LineEdit.MIState, ByteString)) precompile(Base.LineEdit.match_input, (Dict{Char,Any},Base.LineEdit.MIState)) -precompile(Base.LineEdit.match_input, (Function, Base.LineEdit.MIState, Array{Char, 1})) -precompile(Base.LineEdit.match_input, (Function, Base.LineEdit.MIState, Array{Char,1})) +precompile(Base.LineEdit.match_input, (Dict{Char, Any}, Base.LineEdit.MIState, Base.Terminals.TTYTerminal)) +precompile(Base.LineEdit.match_input, (Function, Base.LineEdit.MIState, Base.Terminals.TTYTerminal, Array{Char,1})) precompile(Base.LineEdit.mode, (Base.LineEdit.MIState,)) precompile(Base.LineEdit.move_line_end, (Base.LineEdit.MIState,)) precompile(Base.LineEdit.on_enter, (Base.LineEdit.MIState,)) @@ -104,8 +104,8 @@ precompile(Base.LineEdit.transition, (Base.LineEdit.MIState, Base.LineEdit.Promp precompile(Base.LineEdit.transition, (Base.LineEdit.MIState, Symbol)) precompile(Base.LineEdit.update_key_repeats, (Base.LineEdit.MIState, Array{Char, 1})) precompile(Base.LineEdit.update_key_repeats, (Base.LineEdit.MIState, Array{Char,1})) -precompile(Base.LineEdit.write_prompt, (Base.Terminals.TTYTerminal, Base.LineEdit.PromptState, ASCIIString)) -precompile(Base.LineEdit.write_prompt, (Base.Terminals.TerminalBuffer, Base.LineEdit.PromptState, ASCIIString)) +precompile(Base.LineEdit.write_prompt, (Base.Terminals.TTYTerminal, Base.LineEdit.Prompt)) +precompile(Base.LineEdit.write_prompt, (Base.Terminals.TerminalBuffer, Base.LineEdit.PromptState)) precompile(Base.Multimedia.TextDisplay, (Base.TTY,)) precompile(Base.Multimedia.display, (Int,)) precompile(Base.ProcessGroup, (Int, Array{Any,1}, Array{Any,1})) diff --git a/base/random.jl b/base/random.jl index 7dcd0f26ce1a8..036c1cff90377 100644 --- a/base/random.jl +++ b/base/random.jl @@ -8,11 +8,54 @@ export srand, randn, randn!, randexp, randexp!, randbool, - AbstractRNG, RNG, MersenneTwister + AbstractRNG, RNG, MersenneTwister, RandomDevice abstract AbstractRNG +abstract FloatInterval +type CloseOpen <: FloatInterval end +type Close1Open2 <: FloatInterval end + + +## RandomDevice + +@unix_only begin + + immutable RandomDevice <: AbstractRNG + file::IOStream + + RandomDevice(unlimited::Bool=true) = new(open(unlimited ? "/dev/urandom" : "/dev/random")) + end + + rand {T<:Union(Bool, Base.IntTypes...)}(rd::RandomDevice, ::Type{T}) = read( rd.file, T) + rand!{T<:Union(Bool, Base.IntTypes...)}(rd::RandomDevice, A::Array{T}) = read!(rd.file, A) +end + +@windows_only begin + + immutable RandomDevice <: AbstractRNG + buffer::Vector{UInt128} + + RandomDevice() = new(Array(UInt128, 1)) + end + + function rand{T<:Union(Bool, Base.IntTypes...)}(rd::RandomDevice, ::Type{T}) + win32_SystemFunction036!(rd.buffer) + @inbounds return rd.buffer[1] % T + end + + rand!{T<:Union(Bool, Base.IntTypes...)}(rd::RandomDevice, A::Array{T}) = (win32_SystemFunction036!(A); A) +end + +rand(rng::RandomDevice, ::Type{Close1Open2}) = + reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) + +rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 + + +## MersenneTwister + const MTCacheLength = dsfmt_get_min_array_size() type MersenneTwister <: AbstractRNG @@ -44,10 +87,6 @@ end # precondition: n <= MTCacheLength @inline reserve(r::MersenneTwister, n::Int) = mt_avail(r) < n && gen_rand(r) -abstract FloatInterval -type CloseOpen <: FloatInterval end -type Close1Open2 <: FloatInterval end - # precondition: !mt_empty(r) @inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) @inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 @@ -78,10 +117,8 @@ __init__() = srand() # make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding function make_seed() - -@unix_only begin try - return make_seed("/dev/urandom", 4) + return rand(RandomDevice(), UInt32, 4) catch println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") seed = reinterpret(UInt64, time()) @@ -93,13 +130,6 @@ function make_seed() end end -@windows_only begin - a = zeros(UInt32, 2) - win32_SystemFunction036!(a) - return a -end -end - function make_seed(n::Integer) n < 0 && throw(DomainError()) seed = UInt32[] @@ -174,9 +204,9 @@ rand(r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert((Int...), @inline rand(r::AbstractRNG) = rand(r, CloseOpen) -# MersenneTwister -rand(r::MersenneTwister, ::Type{Float64}) = rand(r, CloseOpen) -rand{T<:Union(Float16, Float32)}(r::MersenneTwister, ::Type{T}) = convert(T, rand(r, Float64)) +# MersenneTwister & RandomDevice +@inline rand(r::Union(RandomDevice,MersenneTwister), ::Type{Float64}) = rand(r, CloseOpen) +rand{T<:Union(Float16, Float32)}(r::Union(RandomDevice,MersenneTwister), ::Type{T}) = convert(T, rand(r, Float64)) ## random integers diff --git a/base/sysimg.jl b/base/sysimg.jl index 67aa85ba7dd77..a33c5a37a52e6 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -294,6 +294,8 @@ function __init__() reinit_stdio() Multimedia.reinit_displays() # since Multimedia.displays uses STDOUT as fallback fdwatcher_init() + early_init() + init_load_path() end include("precompile.jl") diff --git a/contrib/build_sysimg.jl b/contrib/build_sysimg.jl new file mode 100644 index 0000000000000..a73445a06564a --- /dev/null +++ b/contrib/build_sysimg.jl @@ -0,0 +1,157 @@ +#!/usr/bin/env julia + +# Build a system image binary at sysimg_path.dlext. By default, put the system image next to libjulia +# Allow insertion of a userimg via userimg_path. If sysimg_path.dlext is currently loaded into memory, +# don't continue unless force is set to true. Allow targeting of a CPU architecture via cpu_target +const default_sysimg_path = joinpath(dirname(Sys.dlpath("libjulia")),"sys") +function build_sysimg(sysimg_path=default_sysimg_path, cpu_target="native", userimg_path=nothing; force=false) + # Quit out if a sysimg is already loaded and is in the same spot as sysimg_path, unless forcing + sysimg = dlopen_e("sys") + if sysimg != C_NULL + if !force && Sys.dlpath(sysimg) == "$(sysimg_path).$(Sys.dlext)" + println("System image already loaded at $(Sys.dlpath(sysimg)), set force to override") + return + end + end + + # Canonicalize userimg_path before we enter the base_dir + if userimg_path != nothing + userimg_path = abspath(userimg_path) + end + + # Enter base/ and setup some useful paths + base_dir = dirname(Base.find_source_file("sysimg.jl")) + cd(base_dir) do + try + julia = joinpath(JULIA_HOME, "julia") + julia_libdir = dirname(Sys.dlpath("libjulia")) + ld = find_system_linker() + + # Ensure we have write-permissions to wherever we're trying to write to + try + touch("$sysimg_path.$(Sys.dlext)") + catch + err_msg = "Unable to modify $sysimg_path.$(Sys.dlext), ensure parent directory exists " + err_msg *= "and is writable. Absolute paths work best. Do you need to run this with sudo?)" + error( err_msg ) + end + + # Copy in userimg.jl if it exists... + if userimg_path != nothing + if !isreadable(userimg_path) + error("$userimg_path is not readable, ensure it is an absolute path!") + end + cp(userimg_path, "userimg.jl") + end + + # Start by building sys0.{ji,o} + sys0_path = joinpath(dirname(sysimg_path), "sys0") + println("Building sys0.o...") + println("$julia -C $cpu_target --build $sys0_path sysimg.jl") + run(`$julia -C $cpu_target --build $sys0_path sysimg.jl`) + + # Bootstrap off of that to create sys.{ji,o} + println("Building sys.o...") + println("$julia -C $cpu_target --build $sysimg_path -J $sys0_path.ji -f sysimg.jl") + run(`$julia -C $cpu_target --build $sysimg_path -J $sys0_path.ji -f sysimg.jl`) + + # Link sys.o into sys.$(dlext) + FLAGS = ["-L$julia_libdir"] + if OS_NAME == :Darwin + push!(FLAGS, "-dylib") + push!(FLAGS, "-undefined") + push!(FLAGS, "dynamic_lookup") + push!(FLAGS, "-macosx_version_min") + push!(FLAGS, "10.7") + else + if OS_NAME == :Linux + push!(FLAGS, "-shared") + end + push!(FLAGS, "--unresolved-symbols") + push!(FLAGS, "ignore-all") + end + @windows_only append!(FLAGS, ["-L$JULIA_HOME", "-ljulia", "-lssp"]) + + if ld != nothing + println("Linking sys.$(Sys.dlext)") + run(`$ld $FLAGS -o $sysimg_path.$(Sys.dlext) $sysimg_path.o`) + end + + println("System image successfully built at $sysimg_path.$(Sys.dlext)") + @windows_only begin + if convert(VersionNumber, Base.libllvm_version) < v"3.5.0" + LLVM_msg = "Building sys.dll on Windows against LLVM < 3.5.0 can cause incorrect backtraces!" + LLVM_msg *= " Delete generated sys.dll to avoid these problems" + warn( LLVM_msg ) + end + end + + if default_sysimg_path != sysimg_path + println("To run Julia with this image loaded, run: julia -J $sysimg_path.ji") + else + println("Julia will automatically load this system image at next startup") + end + finally + # Cleanup userimg.jl + if isfile("userimg.jl") + rm("userimg.jl") + end + end + end +end + +# Search for a linker to link sys.o into sys.dl_ext. Honor LD environment variable. +function find_system_linker() + if haskey( ENV, "LD" ) + if !success(`$(ENV["LD"]) -v`) + warn("Using linker override $(ENV["LD"]), but unable to run `$(ENV["LD"]) -v`") + end + return ENV["LD"] + end + + # On Windows, check to see if WinRPM is installed, and if so, see if binutils is installed + @windows_only try + using WinRPM + if WinRPM.installed("binutils") + ENV["PATH"] = "$(ENV["PATH"]):$(joinpath(WinRPM.installdir,"usr","$(Sys.ARCH)-w64-mingw32","sys-root","mingw","bin"))" + else + throw() + end + catch + warn("Install Binutils via WinRPM.install(\"binutils\") to generate sys.dll for faster startup times" ) + end + + + # See if `ld` exists + try + if success(`ld -v`) + return "ld" + end + end + + warn( "No supported linker found; startup times will be longer" ) +end + +# When running this file as a script, try to do so with default values. If arguments are passed +# in, use them as the arguments to build_sysimg above +if !isinteractive() + if length(ARGS) > 4 || ("--help" in ARGS || "-h" in ARGS) + println("Usage: build_sysimg.jl [--force] [--help]") + println(" is an absolute, extensionless path to store the system image at") + println(" is an LLVM cpu target to build the system image against") + println(" is the path to a user image to be baked into the system image") + println(" --force Set if you wish to overwrite the default system image") + println(" --help Print out this help text and exit") + println() + println(" Example:") + println(" build_sysimg.jl /usr/local/lib/julia/sys core2 ~/my_usrimg.jl true") + println() + println(" Running this script with no arguments is equivalent to calling it via") + println(" build_sysimg.jl $(default_sysimg_path) native") + return 0 + end + + force_flag = "--force" in ARGS + filter!(x -> x != "--force", ARGS) + build_sysimg(ARGS..., force=force_flag) +end diff --git a/deps/Makefile b/deps/Makefile index 513fb749da3b8..ec2002a99d922 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -279,6 +279,7 @@ else LLVM_FLAGS += --disable-threads endif # USE_INTEL_JITEVENTS +ifeq ($(BUILD_LLDB),1) ifeq ($(USECLANG),1) LLVM_FLAGS += --enable-cxx11 else @@ -287,6 +288,7 @@ endif # USECLANG ifeq ($(LLDB_DISABLE_PYTHON),1) LLVM_CXXFLAGS += -DLLDB_DISABLE_PYTHON endif # LLDB_DISABLE_PYTHON +endif # BUILD_LLDB ifeq ($(ARCH), ppc64) LLVM_CXXFLAGS += -mminimal-toc @@ -548,6 +550,11 @@ check-llvm: llvm-$(LLVM_VER)/build_$(LLVM_BUILDTYPE)/checked install-llvm: $(LLVM_OBJ_TARGET) #todo: LLVM make check target is broken on julia.mit.edu (and really slow elsewhere) +update-llvm: + (cd llvm-$(LLVM_VER); git pull --ff-only) + ([ -d llvm-$(LLVM_VER)/tools/clang ] && (cd llvm-$(LLVM_VER)/tools/clang; git pull --ff-only)) + ([ -d llvm-$(LLVM_VER)/projects/compiler-rt ] && (cd llvm-$(LLVM_VER)/projects/compiler-rt; git pull --ff-only)) + ([ -d llvm-$(LLVM_VER)/tools/lldb ] && (cd llvm-$(LLVM_VER)/tools/lldb; git pull --ff-only)) ## LIBUV ## @@ -1814,4 +1821,4 @@ install-libgit2: $(LIBGIT2_OBJ_TARGET) .PHONY: default compile install cleanall distcleanall \ get-* configure-* compile-* check-* install-* \ - clean-* distclean-* reinstall-* + clean-* distclean-* reinstall-* update-llvm diff --git a/doc/devdocs/julia.rst b/doc/devdocs/julia.rst index df37b96e0775f..e7b7f13fe6b5b 100644 --- a/doc/devdocs/julia.rst +++ b/doc/devdocs/julia.rst @@ -12,3 +12,4 @@ cartesian meta subarrays + sysimg diff --git a/doc/devdocs/sysimg.rst b/doc/devdocs/sysimg.rst new file mode 100644 index 0000000000000..32bc40e58a24c --- /dev/null +++ b/doc/devdocs/sysimg.rst @@ -0,0 +1,35 @@ +********************* +System Image Building +********************* + +Building the Julia system image +------------------------------- + +Julia ships with a preparsed system image containing the contents of the ``Base`` module, named ``sys.ji``. This file is also precompiled into a shared library called ``sys.{so,dll,dylib}`` on as many platforms as possible, so as to give vastly improved startup times. On systems that do not ship with a precompiled system image file, one can be generated from the source files shipped in Julia's ``DATAROOTDIR/julia/base`` folder. + +This operation is useful for multiple reasons. A user may: + +* Build a precompiled shared library system image on a platform that did not ship with one, thereby improving startup times. + +* Modify ``Base``, rebuild the system image and use the new ``Base`` next time Julia is started. + +* Include a ``userimg.jl`` file that includes packages into the system image, thereby creating a system image that has packages embedded into the startup environment. + +Julia now ships with a script that automates the tasks of building the system image, wittingly named ``build_sysimg.jl`` that lives in ``DATAROOTDIR/julia/``. That is, to include it into a current Julia session, type: +:: + + include(joinpath(JULIA_HOME, Base.DATAROOTDIR, "julia", "build_sysimg.jl")) + +This will include a ``build_sysimg()`` function: + +.. function:: build_sysimg(sysimg_path=default_sysimg_path, cpu_target="native", userimg_path=nothing; force=false) + Rebuild the system image. + Store it in ``sysimg_path``, which defaults to a file named ``sys.ji`` that sits in the same folder as ``libjulia.{so,dll,dylib}``. + Use the cpu instruction set given by ``cpu_target``. Valid CPU targets are the same as for the ``-C`` option to ``julia``, or the ``-march`` option to ``gcc``. Defaults to ``native``, which means to use all CPU instructions available on the current processor. + Include the user image file given by ``userimg_path``, which should contain directives such as ``using MyPackage`` to include that package in the new system image. + New system image will not replace an older image unless ``force`` is set to true. + +Note that this file can also be run as a script itself, with command line arguments taking the place of arguments passed to the ``build_sysimg`` function. For example, to build a system image in ``/tmp/sys.{so,dll,dylib}``, with the ``core2`` CPU instruction set, a user image of ``~/userimg.jl`` and ``force`` set to ``true``, one would execute: +:: + + julia build_sysimg.jl /tmp/sys core2 ~/userimg.jl --force \ No newline at end of file diff --git a/doc/manual/embedding.rst b/doc/manual/embedding.rst index a5a0cdb1a73c3..c8bd5900025c7 100644 --- a/doc/manual/embedding.rst +++ b/doc/manual/embedding.rst @@ -18,11 +18,25 @@ We start with a simple C program that initializes Julia and calls some Julia cod int main(int argc, char *argv[]) { + /* optional: randomize the stack guard */ + char a=255, b='\n', c=0; + SWAP_STACK_CHK_GUARD(a,b,c); + + /* required: setup the julia context */ jl_init(NULL); - JL_SET_STACK_BASE; + /* run julia commands */ jl_eval_string("print(sqrt(2.0))"); + /* strongly recommended: notify julia that the + program is about to terminate. this allows + julia time to cleanup pending write requests + and run all finalizers + */ + jl_atexit_hook(); + + /* if the stack guard is set: reset the stack guard */ + SWAP_STACK_CHK_GUARD(a,b,c); return 0; } @@ -30,7 +44,7 @@ In order to build this program you have to put the path to the Julia header into gcc -o test -I$JULIA_DIR/include/julia -L$JULIA_DIR/usr/lib -ljulia test.c -Alternatively, look at the ``embedding.c`` program in the julia source tree in the ``examples/`` folder. +Alternatively, look at the ``embedding.c`` program in the julia source tree in the ``examples/`` folder. The file ``ui/repl.c`` program is another simple example of how to set ``jl_compileropts`` options while linking against libjulia. The first thing that has to be done before calling any other Julia C function is to initialize Julia. This is done by calling ``jl_init``, which takes as argument a C string (``const char*``) to the location where Julia is installed. When the argument is ``NULL``, Julia tries to determine the install location automatically. diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index ee7c767593ee9..7f5ee13592bc2 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -4099,20 +4099,34 @@ The `BigFloat` type implements arbitrary-precision floating-point arithmetic usi Random Numbers -------------- -Random number generation in Julia uses the `Mersenne Twister library `_. Julia has a global RNG, which is used by default. Multiple RNGs can be plugged in using the ``AbstractRNG`` object, which can then be used to have multiple streams of random numbers. Currently, only ``MersenneTwister`` is supported. +Random number generation in Julia uses the `Mersenne Twister library `_ via ``MersenneTwister`` objects. +Julia has a global RNG, which is used by default. Other RNG types can be plugged in by inheriting the ``AbstractRNG`` type; +they can then be used to have multiple streams of random numbers. +Besides ``MersenneTwister``, Julia also provides the ``RandomDevice`` RNG type, which is a wrapper over the OS provided entropy. -Most functions related to random generation accept an optional ``AbstractRNG`` as the first argument, ``rng`` , which defaults to the global one if not provided. Morever, some of them accept optionally dimension specifications ``dims...`` (which can be given as a tuple) to generate arrays of random values. +Most functions related to random generation accept an optional ``AbstractRNG`` as the first argument, ``rng`` , which defaults to the global one if not provided. +Morever, some of them accept optionally dimension specifications ``dims...`` (which can be given as a tuple) to generate arrays of random values. -A ``MersenneTwister`` RNG can generate random numbers of the following types: ``Float16``, ``Float32``, ``Float64``, ``Bool``, ``Int8``, ``UInt8``, ``Int16``, ``UInt16``, ``Int32``, ``UInt32``, ``Int64``, ``UInt64``, ``Int128``, ``UInt128``, ``BigInt`` (or complex numbers of those types). Random floating point numbers are generated uniformly in [0,1). As ``BigInt`` represents unbounded integers, the interval must be specified (e.g. ``rand(big(1:6))``). +A ``MersenneTwister`` or ``RandomDevice`` RNG can generate random numbers of the following types: +``Float16``, ``Float32``, ``Float64``, ``Bool``, ``Int8``, ``UInt8``, ``Int16``, ``UInt16``, +``Int32``, ``UInt32``, ``Int64``, ``UInt64``, ``Int128``, ``UInt128``, ``BigInt`` +(or complex numbers of those types). Random floating point numbers are generated uniformly in [0,1). +As ``BigInt`` represents unbounded integers, the interval must be specified (e.g. ``rand(big(1:6))``). .. function:: srand([rng], [seed]) - Reseed the random number generator. If a ``seed`` is provided, the RNG will give a reproducible sequence of numbers, otherwise Julia will get entropy from the system. The ``seed`` may be a non-negative integer, a vector of ``UInt32`` integers or a filename, in which case the seed is read from a file. + Reseed the random number generator. If a ``seed`` is provided, the RNG will give a reproducible sequence of numbers, otherwise Julia will get entropy from the system. + For ``MersenneTwister``, the ``seed`` may be a non-negative integer, a vector of ``UInt32`` integers or a filename, in which case the seed is read from a file. + ``RandomDevice`` does not support seeding. .. function:: MersenneTwister([seed]) Create a ``MersenneTwister`` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers. +.. function:: RandomDevice() + + Create a ``RandomDevice`` RNG object. Two such objects will always generate different streams of random numbers. + .. function:: rand([rng], [S], [dims...]) Pick a random element or array of random elements from the set of values specified by ``S``; ``S`` can be diff --git a/examples/Makefile b/examples/Makefile index c481347082774..66d7210adfc91 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,9 +1,6 @@ JULIAHOME = $(abspath ..) include $(JULIAHOME)/Make.inc -override CFLAGS += $(JCFLAGS) -override CXXFLAGS += $(JCXXFLAGS) - FLAGS = -Wall -Wno-strict-aliasing -fno-omit-frame-pointer \ -I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(build_includedir) $(CFLAGS) diff --git a/examples/embedding.c b/examples/embedding.c index f9ab53ae9e9e9..4d7f3d29cf90e 100644 --- a/examples/embedding.c +++ b/examples/embedding.c @@ -9,8 +9,9 @@ double my_c_sqrt(double x) int main() { + char a=255, b='\n', c=0; + SWAP_STACK_CHK_GUARD(a,b,c); jl_init(NULL); - JL_SET_STACK_BASE; { // Simple running Julia code @@ -51,14 +52,15 @@ int main() double* xData = jl_array_data(x); - for(size_t i=0; igetType(), true, @@ -255,7 +255,7 @@ static void jl_gen_llvm_gv_array(llvm::Module *mod, SmallVector fsig(0); @@ -4364,7 +4355,7 @@ static void init_julia_llvm_env(Module *m) Function::Create(FunctionType::get(T_void, false), Function::ExternalLinkage, "__stack_chk_fail", m); - //jl__stack_chk_fail->setDoesNotReturn(); + jl__stack_chk_fail->setDoesNotReturn(); add_named_global(jl__stack_chk_fail, (void*)&__stack_chk_fail); jltrue_var = global_to_llvm("jl_true", (void*)&jl_true, m); @@ -4826,7 +4817,6 @@ extern "C" void jl_init_codegen(void) jl_setup_module(engine_module,false); #endif - #if !defined(LLVM_VERSION_MAJOR) || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 0) jl_ExecutionEngine = EngineBuilder(m).setEngineKind(EngineKind::JIT).create(); #ifdef JL_DEBUG_BUILD @@ -4900,9 +4890,9 @@ extern "C" void jl_init_codegen(void) TheTriple, "", #if LLVM35 - strcmp(jl_cpu_string,"native") ? jl_cpu_string : sys::getHostCPUName().data(), + strcmp(jl_compileropts.cpu_target,"native") ? jl_compileropts.cpu_target : sys::getHostCPUName().data(), #else - strcmp(jl_cpu_string,"native") ? jl_cpu_string : "", + strcmp(jl_compileropts.cpu_target,"native") ? jl_compileropts.cpu_target : "", #endif MAttrs); assert(jl_TargetMachine); diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 621b900b2dd0c..00437431db3be 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -33,6 +33,10 @@ #include #endif +#if defined(USE_MCJIT) && !defined(LLVM36) && defined(_OS_DARWIN_) +#include "../deps/llvm-3.5.0/lib/ExecutionEngine/MCJIT/MCJIT.h" +#endif + #include "julia.h" #include "julia_internal.h" @@ -66,6 +70,9 @@ struct FuncInfo { struct ObjectInfo { const object::ObjectFile* object; size_t size; +#ifdef _OS_DARWIN_ + const char *name; +#endif }; #endif @@ -191,21 +198,21 @@ class JuliaJITEventListener: public JITEventListener virtual void NotifyObjectEmitted(const ObjectImage &obj) #endif { - uint64_t SectAddr, Addr; + uint64_t Addr; uint64_t Size; object::SymbolRef::Type SymbolType; -#ifndef _OS_LINUX_ #ifdef LLVM36 object::section_iterator Section = obj.section_begin(); object::section_iterator EndSection = obj.section_end(); - StringRef sName; #else object::section_iterator Section = obj.begin_sections(); object::section_iterator EndSection = obj.end_sections(); + bool isText; #endif +#ifndef _OS_LINUX_ + StringRef sName; #endif #ifdef _OS_WINDOWS_ - StringRef Name; uint64_t SectionAddr; uint64_t SectionSize; #endif @@ -215,28 +222,36 @@ class JuliaJITEventListener: public JITEventListener sym_iter.getType(SymbolType); if (SymbolType != object::SymbolRef::ST_Function) continue; sym_iter.getSize(Size); - sym_iter.getAddress(SectAddr); -#ifndef _OS_LINUX_ + sym_iter.getAddress(Addr); sym_iter.getSection(Section); if (Section == EndSection) continue; - Section = Section->getRelocatedSection(); - if (Section == EndSection) continue; -#ifdef LLVM36 +#if defined(LLVM36) if (!Section->isText()) continue; - Section->getName(sName); - Addr = SectAddr + L.getSectionLoadAddress(sName); #else - Addr = SectAddr; + if (Section->isText(isText) || !isText) continue; #endif +#ifdef _OS_DARWIN_ +#if defined(LLVM36) + Section->getName(sName); + Addr += L.getSectionLoadAddress(sName); #else - Addr = SectAddr; + sym_iter.getName(sName); + Addr = ((MCJIT*)jl_ExecutionEngine)->getSymbolAddress(sName, true); + if (!Addr && sName[0] == '_') { + sName = sName.substr(1); + Addr = ((MCJIT*)jl_ExecutionEngine)->getSymbolAddress(sName, true); + } + if (!Addr) continue; #endif -#ifdef _OS_WINDOWS_ - sym_iter.getName(Name); +#elif defined(_OS_WINDOWS_) Section->getAddress(SectionAddr); Section->getSize(SectionSize); + sym_iter.getName(sName); +#ifndef _CPU_X86_ + if (sName[0] == '_') sName = sName.substr(1); +#endif create_PRUNTIME_FUNCTION( - (uint8_t*)(intptr_t)Addr, (size_t)Size, Name, + (uint8_t*)(intptr_t)Addr, (size_t)Size, sName, (uint8_t*)(intptr_t)SectionAddr, (size_t)SectionSize); #endif const object::ObjectFile *objfile = @@ -245,7 +260,11 @@ class JuliaJITEventListener: public JITEventListener #else obj.getObjectFile(); #endif - ObjectInfo tmp = {objfile, (size_t)Size}; + ObjectInfo tmp = {objfile, (size_t)Size +#ifdef _OS_DARWIN_ + ,strdup(sName.data()) +#endif + }; objectmap[Addr] = tmp; } #else //LLVM34 @@ -278,16 +297,19 @@ class JuliaJITEventListener: public JITEventListener extern "C" const char *jl_demangle(const char *name) { - const char *start = name; - const char *end = start; + const char *start = name + 6; + const char *end = name + strlen(name); char *ret; - while ((*start++ != '_') && (*start != '\0')); - if (*name == '\0') goto done; - while ((*end++ != ';') && (*end != '\0')); - if (*name == '\0') goto done; - ret = (char*)malloc(end-start); - memcpy(ret,start,end-start-1); - ret[end-start-1] = '\0'; + if (strncmp(name, "julia_", 6)) goto done; + if (*start == '\0') goto done; + while (*(--end) != '_') { + char c = *end; + if (c < '0' || c > '9') goto done; + } + if (end <= start) goto done; + ret = (char*)malloc(end-start+1); + memcpy(ret,start,end-start); + ret[end-start] = '\0'; return ret; done: return strdup(name); @@ -433,8 +455,6 @@ void jl_getDylibFunctionInfo(const char **name, size_t *line, const char **filen } else if (*fromC) { // No debug info, use dll name instead *filename = fname; - } else { - *filename = ""; } jl_in_stackwalk = 0; #else // ifdef _OS_WINDOWS_ @@ -602,22 +622,26 @@ void jl_getFunctionInfo(const char **name, size_t *line, const char **filename, std::map &objmap = jl_jit_events->getObjectMap(); std::map::iterator it = objmap.lower_bound(pointer); - if (it == objmap.end() || (pointer - it->first) > it->second.size) - return jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); - + if (it != objmap.end() && (intptr_t)(*it).first + (*it).second.size > pointer) { +#if defined(_OS_DARWIN_) + *name = jl_demangle((*it).second.name); + DIContext *context = NULL; // current versions of MCJIT can't handle MachO relocations +#else #ifdef LLVM36 - DIContext *context = DIContext::getDWARFContext(*it->second.object); + DIContext *context = DIContext::getDWARFContext(*it->second.object); #else - DIContext *context = DIContext::getDWARFContext(const_cast(it->second.object)); + DIContext *context = DIContext::getDWARFContext(const_cast(it->second.object)); #endif - lookup_pointer(context, name, line, filename, pointer, 1, fromC); +#endif + lookup_pointer(context, name, line, filename, pointer, 1, fromC); + return; + } #else // !USE_MCJIT - // Without MCJIT we use the FuncInfo structure containing address maps std::map &info = jl_jit_events->getMap(); std::map::iterator it = info.lower_bound(pointer); - if (it != info.end() && (size_t)(*it).first + (*it).second.lengthAdr >= pointer) { + if (it != info.end() && (intptr_t)(*it).first + (*it).second.lengthAdr >= pointer) { // We do this to hide the jlcall wrappers when getting julia backtraces, // but it is still good to have them for regular lookup of C frames. if (skipC && (*it).second.lines.empty()) { @@ -665,11 +689,10 @@ void jl_getFunctionInfo(const char **name, size_t *line, const char **filename, if (*line == (size_t) -1) { *line = prev.Loc.getLine(); } - } - else { - jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); + return; } #endif // USE_MCJIT + jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); } int jl_get_llvmf_info(size_t fptr, uint64_t *symsize, @@ -683,22 +706,22 @@ int jl_get_llvmf_info(size_t fptr, uint64_t *symsize, std::map &fmap = jl_jit_events->getMap(); std::map::iterator fit = fmap.find(fptr); - if (fit == fmap.end()) { - return 0; + if (fit != fmap.end()) { + *symsize = fit->second.lengthAdr; + *lines = fit->second.lines; + return 1; } - *symsize = fit->second.lengthAdr; - *lines = fit->second.lines; - return 1; + return 0; #else // MCJIT version - std::map objmap = jl_jit_events->getObjectMap(); + std::map &objmap = jl_jit_events->getObjectMap(); std::map::iterator fit = objmap.find(fptr); - if (fit == objmap.end()) { - return 0; + if (fit != objmap.end()) { + *symsize = fit->second.size; + *object = fit->second.object; + return 1; } - *symsize = fit->second.size; - *object = fit->second.object; - return 1; + return 0; #endif } diff --git a/src/disasm.cpp b/src/disasm.cpp index 528d33b88be68..fdd998f84b252 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -482,7 +482,7 @@ void jl_dump_function_asm(const char *Fptr, size_t Fsize, #ifdef LLVM35 if (MCIA->evaluateBranch(Inst, Index, insSize, addr)) #else - if ((addr = MCIA->evaluateBranch(Inst, Index, insSize)) != -1) + if ((addr = MCIA->evaluateBranch(Inst, Index, insSize)) != (uint64_t)-1) #endif DisInfo.insertAddress(addr); } diff --git a/src/dlload.c b/src/dlload.c index ef5332696e176..33b6249e34ff2 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -116,7 +116,7 @@ static uv_lib_t *jl_load_dynamic_library_(char *modname, unsigned flags, int thr ext = extensions[i]; path[0] = '\0'; handle->handle = NULL; - if (dl_path[len-1] == PATHSEP) + if (dl_path[len-1] == PATHSEPSTRING[0]) snprintf(path, PATHBUF, "%s%s%s", dl_path, modname, ext); else snprintf(path, PATHBUF, "%s" PATHSEPSTRING "%s%s", dl_path, modname, ext); diff --git a/src/dump.c b/src/dump.c index b9ea91b53e8f4..9e0ff5f0b30a7 100644 --- a/src/dump.c +++ b/src/dump.c @@ -165,7 +165,6 @@ jl_value_t ***sysimg_gvars = NULL; extern int globalUnique; extern void jl_cpuid(int32_t CPUInfo[4], int32_t InfoType); -extern const char *jl_cpu_string; uv_lib_t *jl_sysimg_handle = NULL; uint64_t jl_sysimage_base = 0; #ifdef _OS_WINDOWS_ @@ -185,7 +184,7 @@ static void jl_load_sysimg_so(char *fname) sysimg_gvars = (jl_value_t***)jl_dlsym(jl_sysimg_handle, "jl_sysimg_gvars"); globalUnique = *(size_t*)jl_dlsym(jl_sysimg_handle, "jl_globalUnique"); const char *cpu_target = (const char*)jl_dlsym(jl_sysimg_handle, "jl_sysimg_cpu_target"); - if (strcmp(cpu_target,jl_cpu_string) != 0) + if (strcmp(cpu_target,jl_compileropts.cpu_target) != 0) jl_error("Julia and the system image were compiled for different architectures.\n" "Please delete or regenerate sys.{so,dll,dylib}.\n"); uint32_t info[4]; @@ -193,13 +192,13 @@ static void jl_load_sysimg_so(char *fname) if (strcmp(cpu_target, "native") == 0) { uint64_t saved_cpuid = *(uint64_t*)jl_dlsym(jl_sysimg_handle, "jl_sysimg_cpu_cpuid"); if (saved_cpuid != (((uint64_t)info[2])|(((uint64_t)info[3])<<32))) - jl_error("Target architecture mismatch. Please delete or regenerate sys.{so,dll,dylib}."); + jl_error("Target architecture mismatch. Please delete or regenerate sys.{so,dll,dylib}.\n"); } else if (strcmp(cpu_target,"core2") == 0) { int HasSSSE3 = (info[2] & 1<<9); if (!HasSSSE3) jl_error("The current host does not support SSSE3, but the system image was compiled for Core2.\n" - "Please delete or regenerate sys.{so,dll,dylib}."); + "Please delete or regenerate sys.{so,dll,dylib}.\n"); } #ifdef _OS_WINDOWS_ jl_sysimage_base = (intptr_t)jl_sysimg_handle->handle; @@ -452,8 +451,6 @@ static void jl_serialize_module(ios_t *s, jl_module_t *m) jl_serialize_value(s, m->parent); if (ref_only) return; - // set on every startup; don't save value - jl_sym_t *jhsym = jl_symbol("JULIA_HOME"); size_t i; void **table = m->bindings.table; for(i=1; i < m->bindings.size; i+=2) { @@ -461,12 +458,7 @@ static void jl_serialize_module(ios_t *s, jl_module_t *m) jl_binding_t *b = (jl_binding_t*)table[i]; if (b->owner == m || m != jl_main_module) { jl_serialize_value(s, b->name); - if (table[i-1] == jhsym && m == jl_core_module) { - jl_serialize_value(s, NULL); - } - else { - jl_serialize_value(s, b->value); - } + jl_serialize_value(s, b->value); jl_serialize_value(s, b->type); jl_serialize_value(s, b->owner); write_int8(s, (b->constp<<2) | (b->exportp<<1) | (b->imported)); @@ -1305,7 +1297,7 @@ void jl_deserialize_lambdas_from_mod(ios_t *s) extern jl_array_t *jl_module_init_order; -DLLEXPORT void jl_save_system_image(char *fname) +DLLEXPORT void jl_save_system_image(const char *fname) { jl_gc_collect(); jl_gc_collect(); @@ -1337,10 +1329,8 @@ DLLEXPORT void jl_save_system_image(char *fname) // save module initialization order if (jl_module_init_order != NULL) { for(i=0; i < jl_array_len(jl_module_init_order); i++) { - // NULL out any modules that weren't saved - jl_value_t *mod = jl_cellref(jl_module_init_order, i); - if (ptrhash_get(&backref_table, mod) == HT_NOTFOUND) - jl_cellset(jl_module_init_order, i, NULL); + // verify that all these modules were saved + assert(ptrhash_get(&backref_table, jl_cellref(jl_module_init_order, i)) != HT_NOTFOUND); } } jl_serialize_value(&f, jl_module_init_order); @@ -1359,12 +1349,37 @@ extern void jl_get_builtin_hooks(void); extern void jl_get_system_hooks(void); extern void jl_get_uv_hooks(); +// Takes in a path of the form "usr/lib/julia/sys.ji", such as passed in to jl_restore_system_image() DLLEXPORT -void jl_restore_system_image(char *fname) +const char * jl_get_system_image_cpu_target(char *fname) +{ + // If passed NULL, don't even bother + if (!fname) + return NULL; + + // First, get "sys" from "sys.ji" + char *fname_shlib = (char*)alloca(strlen(fname)); + strcpy(fname_shlib, fname); + char *fname_shlib_dot = strrchr(fname_shlib, '.'); + if (fname_shlib_dot != NULL) + *fname_shlib_dot = 0; + + // Get handle to sys.so + uv_lib_t * sysimg_handle = jl_load_dynamic_library_e(fname_shlib, JL_RTLD_DEFAULT | JL_RTLD_GLOBAL); + + // Return jl_sysimg_cpu_target if we can + if (sysimg_handle) + return (const char *)jl_dlsym(sysimg_handle, "jl_sysimg_cpu_target"); + + // If something goes wrong, return NULL + return NULL; +} + +DLLEXPORT +void jl_restore_system_image(const char *fname) { ios_t f; - char *fpath = fname; - if (ios_file(&f, fpath, 1, 0, 0, 0) == NULL) { + if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) { JL_PRINTF(JL_STDERR, "System image file \"%s\" not found\n", fname); exit(1); } @@ -1432,14 +1447,10 @@ void jl_restore_system_image(char *fname) //ios_printf(ios_stderr, "backref_list.len = %d\n", backref_list.len); arraylist_free(&backref_list); ios_close(&f); - if (fpath != fname) free(fpath); #ifdef JL_GC_MARKSWEEP if (en) jl_gc_enable(); #endif - // restore the value of our "magic" JULIA_HOME variable/constant - jl_get_binding_wr(jl_core_module, jl_symbol("JULIA_HOME"))->value = - jl_cstr_to_string(julia_home); mode = last_mode; jl_update_all_fptrs(); } @@ -1543,7 +1554,7 @@ jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data) } DLLEXPORT -int jl_save_new_module(char *fname, jl_module_t *mod) +int jl_save_new_module(const char *fname, jl_module_t *mod) { ios_t f; if (ios_file(&f, fname, 1, 1, 1, 1) == NULL) { @@ -1583,7 +1594,7 @@ jl_function_t *jl_method_cache_insert(jl_methtable_t *mt, jl_tuple_t *type, jl_function_t *method); DLLEXPORT -jl_module_t *jl_restore_new_module(char *fname) +jl_module_t *jl_restore_new_module(const char *fname) { ios_t f; if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) { diff --git a/src/gc.c b/src/gc.c index 1b8223d0027fc..42b6bbad33aff 100644 --- a/src/gc.c +++ b/src/gc.c @@ -712,7 +712,7 @@ static void gc_mark_task(jl_task_t *ta, int d) gc_mark_stack(jl_pgcstack, offset, d); } else { - offset = (char *)ta->stkbuf - ((char *)ta->stackbase - ta->ssize); + offset = (char *)ta->stkbuf - ((char *)jl_stackbase - ta->ssize); gc_mark_stack(ta->gcstack, offset, d); } #else diff --git a/src/init.c b/src/init.c index 3b19d39f764b2..2a11c995350a9 100644 --- a/src/init.c +++ b/src/init.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -43,6 +43,12 @@ extern "C" { #endif +#ifdef _MSC_VER +DLLEXPORT char * dirname(char *); +#else +#include +#endif + #ifdef _OS_WINDOWS_ #define WIN32_LEAN_AND_MEAN // Copied from MINGW_FLOAT_H which may not be found due to a collision with the builtin gcc float.h @@ -80,8 +86,13 @@ DLLEXPORT void jlbacktrace(); DLLEXPORT void gdbbacktrace(); DLLEXPORT void gdblookup(ptrint_t ip); -char *julia_home = NULL; -jl_compileropts_t jl_compileropts = { NULL, // build_path +static const char system_image_path[256] = JL_SYSTEM_IMAGE_PATH; + +jl_compileropts_t jl_compileropts = { NULL, // julia_home + NULL, // julia_bin + NULL, // build_path + system_image_path, // image_file + NULL, // cpu_target ("native", "core2", etc...) 0, // code_coverage 0, // malloc_log JL_COMPILEROPT_CHECK_BOUNDS_DEFAULT, @@ -94,6 +105,10 @@ jl_compileropts_t jl_compileropts = { NULL, // build_path int jl_boot_file_loaded = 0; int exit_on_sigint = 0; +void *__stack_chk_guard = NULL; +DLLEXPORT unsigned char *jl_stack_chk_guard = + (unsigned char *)&__stack_chk_guard; + char *jl_stack_lo; char *jl_stack_hi; size_t jl_page_size; @@ -428,7 +443,7 @@ static void jl_uv_exitcleanup_walk(uv_handle_t *handle, void *arg) void jl_write_coverage_data(void); void jl_write_malloc_log(void); -DLLEXPORT void uv_atexit_hook() +DLLEXPORT void jl_atexit_hook() { #if defined(JL_GC_MARKSWEEP) && defined(GC_FINAL_STATS) jl_print_gc_stats(JL_STDERR); @@ -739,10 +754,126 @@ kern_return_t catch_exception_raise(mach_port_t exception_port, #endif -void julia_init(char *imageFile) +static int isabspath(const char *in) { +#ifdef _OS_WINDOWS_ + char c0 = in[0]; + if (c0 == '/' || c0 == '\\') { + return 1; // absolute path relative to %CD% (current drive), or UNC + } + else { + int s = strlen(in); + if (s > 2) { + char c1 = in[1]; + char c2 = in[2]; + if (c1 == ':' && (c2 == '/' || c2 == '\\')) return 1; // absolute path + } + } +#else + if (jl_compileropts.image_file[0] == '/') return 1; // absolute path +#endif + return 0; // relative path +} + +static char *abspath(const char *in) +{ // compute an absolute path location, so that chdir doesn't change the file reference +#ifndef _OS_WINDOWS_ + char *out = realpath(in, NULL); + if (!out) { + if (in[0] == PATHSEPSTRING[0]) { + out = strdup(in); + } + else { + size_t path_size = PATH_MAX; + size_t len = strlen(in); + char *path = (char*)malloc(PATH_MAX); + if (uv_cwd(path, &path_size)) { + ios_printf(ios_stderr, "fatal error: unexpected error while retrieving current working directory\n"); + exit(1); + } + if (path_size + len + 1 >= PATH_MAX) { + ios_printf(ios_stderr, "fatal error: current working directory path too long\n"); + exit(1); + } + path[path_size-1] = PATHSEPSTRING[0]; + memcpy(path+path_size, in, len+1); + out = strdup(path); + } + } +#else + DWORD n = GetFullPathName(in, 0, NULL, NULL); + if (n <= 0) { + ios_printf(ios_stderr, "fatal error: jl_compileropts.image_file path too long or GetFullPathName failed\n"); + exit(1); + } + char *out = (char*)malloc(n); + DWORD m = GetFullPathName(in, n, out, NULL); + if (n != m + 1) { + ios_printf(ios_stderr, "fatal error: jl_compileropts.image_file path too long or GetFullPathName failed\n"); + exit(1); + } +#endif + return out; +} + +static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) +{ // this function resolves the paths in jl_compileropts to absolute file locations as needed + // and it replaces the pointers to `julia_home`, `julia_bin`, `image_file`, and `build_path` + // it may fail, print an error, and exit(1) if any of these paths are longer than PATH_MAX + // + // note: if you care about lost memory, you should call the appropriate `free()` function + // on the original pointer for each `char*` you've inserted into `jl_compileropts`, after + // calling `julia_init()` + char *free_path = (char*)malloc(PATH_MAX); + size_t path_size = PATH_MAX; + if (uv_exepath(free_path, &path_size)) { + ios_printf(ios_stderr, "fatal error: unexpected error while retrieving exepath\n"); + exit(1); + } + if (path_size >= PATH_MAX) { + ios_printf(ios_stderr, "fatal error: jl_compileropts.julia_bin path too long\n"); + exit(1); + } + jl_compileropts.julia_bin = strdup(free_path); + if (!jl_compileropts.julia_home) { + jl_compileropts.julia_home = getenv("JULIA_HOME"); + if (!jl_compileropts.julia_home) { + jl_compileropts.julia_home = dirname(free_path); + } + } + if (jl_compileropts.julia_home) + jl_compileropts.julia_home = abspath(jl_compileropts.julia_home); + free(free_path); + free_path = NULL; + if (jl_compileropts.image_file) { + if (rel == JL_IMAGE_JULIA_HOME && !isabspath(jl_compileropts.image_file)) { + // build time path, relative to JULIA_HOME + free_path = (char*)malloc(PATH_MAX); + int n = snprintf(free_path, PATH_MAX, "%s" PATHSEPSTRING "%s", + jl_compileropts.julia_home, jl_compileropts.image_file); + if (n >= PATH_MAX || n < 0) { + ios_printf(ios_stderr, "fatal error: jl_compileropts.image_file path too long\n"); + exit(1); + } + jl_compileropts.image_file = free_path; + } + if (jl_compileropts.image_file) + jl_compileropts.image_file = abspath(jl_compileropts.image_file); + if (free_path) { + free(free_path); + free_path = NULL; + } + } + if (jl_compileropts.build_path) + jl_compileropts.build_path = abspath(jl_compileropts.build_path); +} + +void _julia_init(JL_IMAGE_SEARCH rel) +{ + libsupport_init(); jl_io_loop = uv_default_loop(); // this loop will internal events (spawning process etc.), // best to call this first, since it also initializes libuv + jl_resolve_sysimg_location(rel); jl_page_size = jl_getpagesize(); jl_arr_xtralloc_limit = uv_get_total_memory() / 100; // Extra allocation limited to 1% of total RAM jl_find_stack_bottom(); @@ -765,11 +896,11 @@ void julia_init(char *imageFile) if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (PHANDLE)&hMainThread, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - JL_PRINTF(JL_STDERR, "WARNING: failed to access handle to main thread\n"); + ios_printf(ios_stderr, "WARNING: failed to access handle to main thread\n"); } SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); if (!SymInitialize(GetCurrentProcess(), NULL, 1)) { - JL_PRINTF(JL_STDERR, "WARNING: failed to initalize stack walk info\n"); + ios_printf(ios_stderr, "WARNING: failed to initialize stack walk info\n"); } needsSymRefreshModuleList = 0; uv_lib_t jl_dbghelp; @@ -810,12 +941,20 @@ void julia_init(char *imageFile) jl_init_frontend(); jl_init_types(); jl_init_tasks(jl_stack_lo, jl_stack_hi-jl_stack_lo); + + // If we are able to load the imageFile and get a cpu_target, use that unless user has overridden + if (jl_compileropts.cpu_target == NULL) { + const char * sysimg_cpu_target = jl_get_system_image_cpu_target(imageFile); + + // If we can't load anything from the sysimg, default to native + jl_compileropts.cpu_target = sysimg_cpu_target ? sysimg_cpu_target : "native"; + } jl_init_codegen(); jl_an_empty_cell = (jl_value_t*)jl_alloc_cell_1d(0); jl_init_serializer(); - if (!imageFile) { + if (!jl_compileropts.image_file) { jl_core_module = jl_new_module(jl_symbol("Core")); jl_init_intrinsic_functions(); jl_init_primitives(); @@ -830,16 +969,11 @@ void julia_init(char *imageFile) jl_get_builtin_hooks(); jl_boot_file_loaded = 1; jl_init_box_caches(); - // Core.JULIA_HOME is a "magic" constant, we set it at runtime here - // since its value gets excluded from the system image - jl_set_const(jl_core_module, jl_symbol("JULIA_HOME"), - jl_cstr_to_string(julia_home)); - jl_module_export(jl_core_module, jl_symbol("JULIA_HOME")); } - if (imageFile) { + if (jl_compileropts.image_file) { JL_TRY { - jl_restore_system_image(imageFile); + jl_restore_system_image(jl_compileropts.image_file); } JL_CATCH { JL_PRINTF(JL_STDERR, "error during init:\n"); @@ -873,7 +1007,6 @@ void julia_init(char *imageFile) jl_current_module = jl_main_module; jl_root_task->current_module = jl_current_module; - #ifndef _OS_WINDOWS_ signal_stack = malloc(sig_stack_size); struct sigaction actf; @@ -882,11 +1015,11 @@ void julia_init(char *imageFile) actf.sa_handler = fpe_handler; actf.sa_flags = 0; if (sigaction(SIGFPE, &actf, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) { - JL_PRINTF(JL_STDERR, "Couldn't set SIGPIPE\n"); + JL_PRINTF(JL_STDERR, "fatal error: Couldn't set SIGPIPE\n"); jl_exit(1); } #if defined (_OS_DARWIN_) @@ -919,7 +1052,7 @@ void julia_init(char *imageFile) ss.ss_size = sig_stack_size; ss.ss_sp = signal_stack; if (sigaltstack(&ss, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaltstack: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaltstack: %s\n", strerror(errno)); jl_exit(1); } @@ -929,7 +1062,7 @@ void julia_init(char *imageFile) act.sa_sigaction = segv_handler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction(SIGSEGV, &act, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } #endif // defined(_OS_DARWIN_) @@ -939,49 +1072,50 @@ void julia_init(char *imageFile) act_die.sa_sigaction = sigdie_handler; act_die.sa_flags = SA_SIGINFO; if (sigaction(SIGINFO, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGBUS, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGILL, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGTERM, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGABRT, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGQUIT, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGSYS, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } if (sigaction(SIGPIPE, &act_die, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } #else // defined(_OS_WINDOWS_) if (signal(SIGFPE, (void (__cdecl *)(int))fpe_handler) == SIG_ERR) { - JL_PRINTF(JL_STDERR, "Couldn't set SIGFPE\n"); + JL_PRINTF(JL_STDERR, "fatal error: Couldn't set SIGFPE\n"); jl_exit(1); } + SetUnhandledExceptionFilter(exception_handler); #endif #ifdef JL_GC_MARKSWEEP jl_gc_enable(); #endif - if (imageFile) + if (jl_compileropts.image_file) jl_init_restored_modules(); jl_install_sigint_handler(); @@ -998,11 +1132,10 @@ DLLEXPORT void jl_install_sigint_handler() act.sa_sigaction = sigint_handler; act.sa_flags = SA_SIGINFO; if (sigaction(SIGINT, &act, NULL) < 0) { - JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); + JL_PRINTF(JL_STDERR, "fatal error: sigaction: %s\n", strerror(errno)); jl_exit(1); } #endif - //printf("sigint installed\n"); } extern int asprintf(char **str, const char *fmt, ...); @@ -1010,22 +1143,9 @@ extern void *__stack_chk_guard; void jl_compile_all(void); -DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char *av[])) +DLLEXPORT void julia_save() { -#if defined(_OS_WINDOWS_) - SetUnhandledExceptionFilter(exception_handler); -#endif - unsigned char *p = (unsigned char *)&__stack_chk_guard; - char a = p[sizeof(__stack_chk_guard)-1]; - char b = p[sizeof(__stack_chk_guard)-2]; - char c = p[0]; - /* If you have the ability to generate random numbers in your kernel then use them */ - p[sizeof(__stack_chk_guard)-1] = 255; - p[sizeof(__stack_chk_guard)-2] = '\n'; - p[0] = 0; - JL_SET_STACK_BASE; - int ret = pmain(argc, argv); - char *build_path = jl_compileropts.build_path; + const char *build_path = jl_compileropts.build_path; if (build_path) { if (jl_compileropts.compile_enabled == JL_COMPILEROPT_COMPILE_ALL) jl_compile_all(); @@ -1056,10 +1176,6 @@ DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char * ios_printf(ios_stderr,"\nFATAL: failed to create string for .ji build path\n"); } } - p[sizeof(__stack_chk_guard)-1] = a; - p[sizeof(__stack_chk_guard)-2] = b; - p[0] = c; - return ret; } jl_function_t *jl_typeinf_func=NULL; diff --git a/src/jl_uv.c b/src/jl_uv.c index 37615cbd156c8..6b68a895765e5 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -686,14 +686,10 @@ char *jl_bufptr(ios_t *s) return s->buf; } -DLLEXPORT void uv_atexit_hook(); DLLEXPORT void jl_exit(int exitcode) { - /*if (jl_io_loop) { - jl_process_events(&jl_io_loop); - }*/ uv_tty_reset_mode(); - uv_atexit_hook(); + jl_atexit_hook(); exit(exitcode); } diff --git a/src/jlapi.c b/src/jlapi.c index a3eb39b7e0000..006482e50d921 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -22,24 +22,6 @@ DLLEXPORT char * __cdecl basename(char *); #include #endif -DLLEXPORT char *jl_locate_sysimg(char *jlhome, char *imgpath) -{ - if (jlhome == NULL) { - char *julia_path = (char*)malloc(512); - size_t path_size = 512; - uv_exepath(julia_path, &path_size); - julia_home = strdup(dirname(julia_path)); - free(julia_path); - } - else { - julia_home = jlhome; - } - char path[512]; - snprintf(path, sizeof(path), "%s%s%s", - julia_home, PATHSEPSTRING, imgpath); - return strdup(path); -} - DLLEXPORT void *jl_eval_string(char *str); int jl_is_initialized(void) { return jl_main_module!=NULL; } @@ -54,32 +36,26 @@ DLLEXPORT void jl_init_with_image(char *julia_home_dir, char *image_relative_pat { if (jl_is_initialized()) return; libsupport_init(); - if (image_relative_path == NULL) - image_relative_path = JL_SYSTEM_IMAGE_PATH; - char *image_file = jl_locate_sysimg(julia_home_dir, image_relative_path); - julia_init(image_file); - jl_set_const(jl_core_module, jl_symbol("JULIA_HOME"), - jl_cstr_to_string(julia_home)); - jl_module_export(jl_core_module, jl_symbol("JULIA_HOME")); - jl_eval_string("Base.early_init()"); + jl_compileropts.julia_home = julia_home_dir; + if (image_relative_path != NULL) + jl_compileropts.image_file = image_relative_path; + julia_init(JL_IMAGE_JULIA_HOME); + //TODO: these should be part of Multi.__init__() + //currently, we have them here since we may not want them + //getting unconditionally set from Base.__init__() + jl_eval_string("Base.init_parallel()"); + jl_eval_string("Base.init_bind_addr(ARGS)"); jl_eval_string("Base.init_head_sched()"); - jl_eval_string("Base.init_load_path()"); jl_exception_clear(); } DLLEXPORT void jl_init(char *julia_home_dir) { - jl_init_with_image(julia_home_dir, JL_SYSTEM_IMAGE_PATH); + jl_init_with_image(julia_home_dir, NULL); } DLLEXPORT void *jl_eval_string(char *str) { -#ifdef COPY_STACKS - int outside_task = (jl_root_task->stackbase == NULL); - if (outside_task) { - JL_SET_STACK_BASE; - } -#endif jl_value_t *r; JL_TRY { jl_value_t *ast = jl_parse_input_line(str); @@ -92,11 +68,6 @@ DLLEXPORT void *jl_eval_string(char *str) //jl_show(jl_stderr_obj(), jl_exception_in_transit); r = NULL; } -#ifdef COPY_STACKS - if (outside_task) { - jl_root_task->stackbase = NULL; - } -#endif return r; } @@ -273,6 +244,21 @@ DLLEXPORT int jl_is_debugbuild(void) #endif } +DLLEXPORT jl_value_t *jl_get_julia_home(void) +{ + return jl_cstr_to_string(jl_compileropts.julia_home); +} + +DLLEXPORT jl_value_t *jl_get_julia_bin(void) +{ + return jl_cstr_to_string(jl_compileropts.julia_bin); +} + +DLLEXPORT jl_value_t *jl_get_image_file(void) +{ + return jl_cstr_to_string(jl_compileropts.image_file); +} + #ifdef __cplusplus } #endif diff --git a/src/julia.expmap b/src/julia.expmap index 08f54c22f5a0c..40edbbba6a1dc 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -1,6 +1,7 @@ { global: __asan*; + __stack_chk_guard; alloc_*w; allocobj; asprintf; diff --git a/src/julia.h b/src/julia.h index 5311e5a321893..acc32853d64c5 100644 --- a/src/julia.h +++ b/src/julia.h @@ -814,7 +814,6 @@ DLLEXPORT jl_value_t *jl_eqtable_get(jl_array_t *h, void *key, jl_value_t *deflt DLLEXPORT int jl_errno(void); DLLEXPORT void jl_set_errno(int e); DLLEXPORT int32_t jl_stat(const char *path, char *statbuf); -DLLEXPORT void NORETURN jl_exit(int status); DLLEXPORT int jl_cpu_cores(void); DLLEXPORT long jl_getpagesize(void); DLLEXPORT long jl_getallocationgranularity(void); @@ -856,17 +855,24 @@ DLLEXPORT void jl_exception_clear(void); } // initialization functions -DLLEXPORT void julia_init(char *imageFile); -DLLEXPORT int julia_trampoline(int argc, char *argv[], int (*pmain)(int ac,char *av[])); +typedef enum { + JL_IMAGE_CWD = 0, + JL_IMAGE_JULIA_HOME = 1, + //JL_IMAGE_LIBJULIA = 2, +} JL_IMAGE_SEARCH; +DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel); DLLEXPORT void jl_init(char *julia_home_dir); DLLEXPORT void jl_init_with_image(char *julia_home_dir, char *image_relative_path); DLLEXPORT int jl_is_initialized(void); -DLLEXPORT extern char *julia_home; +DLLEXPORT int julia_trampoline(int argc, char *argv[], int (*pmain)(int ac,char *av[])); +DLLEXPORT void jl_atexit_hook(void); +DLLEXPORT void NORETURN jl_exit(int status); -DLLEXPORT void jl_save_system_image(char *fname); -DLLEXPORT void jl_restore_system_image(char *fname); -DLLEXPORT int jl_save_new_module(char *fname, jl_module_t *mod); -DLLEXPORT jl_module_t *jl_restore_new_module(char *fname); +DLLEXPORT const char * jl_get_system_image_cpu_target(const char *fname); +DLLEXPORT void jl_save_system_image(const char *fname); +DLLEXPORT void jl_restore_system_image(const char *fname); +DLLEXPORT int jl_save_new_module(const char *fname, jl_module_t *mod); +DLLEXPORT jl_module_t *jl_restore_new_module(const char *fname); void jl_init_restored_modules(); // front end interface @@ -990,22 +996,6 @@ DLLEXPORT jl_value_t *jl_call3(jl_function_t *f, jl_value_t *a, jl_value_t *b, j // interfacing with Task runtime DLLEXPORT void jl_yield(); -DLLEXPORT void jl_handle_stack_start(); - -#ifdef COPY_STACKS -// initialize base context of root task -extern DLLEXPORT jl_jmp_buf jl_base_ctx; -#define JL_SET_STACK_BASE \ - do { \ - int __stk; \ - jl_root_task->stackbase = (char*)&__stk; \ - if (jl_setjmp(jl_base_ctx, 1)) { \ - jl_handle_stack_start(); \ - } \ - } while (0) -#else -#define JL_SET_STACK_BASE -#endif // gc ------------------------------------------------------------------------- @@ -1149,10 +1139,9 @@ typedef struct _jl_task_t { jl_value_t *exception; jl_function_t *start; jl_jmp_buf ctx; - union { - void *stackbase; - void *stack; - }; +#ifndef COPY_STACKS + void *stack; +#endif size_t bufsz; void *stkbuf; size_t ssize; @@ -1328,7 +1317,11 @@ void show_execution_point(char *filename, int lno); // compiler options ----------------------------------------------------------- typedef struct { - char *build_path; + const char *julia_home; + const char *julia_bin; + const char *build_path; + const char *image_file; + const char *cpu_target; int8_t code_coverage; int8_t malloc_log; int8_t check_bounds; @@ -1356,6 +1349,23 @@ extern DLLEXPORT jl_compileropts_t jl_compileropts; #define JL_COMPILEROPT_DUMPBITCODE_ON 1 #define JL_COMPILEROPT_DUMPBITCODE_OFF 2 +/* If you have the ability to generate random numbers +* in your kernel then they should be used here */ +DLLEXPORT extern unsigned char *jl_stack_chk_guard; +#define SWAP_STACK_CHK_GUARD(a,b,c) do { \ + a ^= jl_stack_chk_guard[sizeof(void*)-1]; \ + jl_stack_chk_guard[sizeof(void*)-1] ^= a; \ + a ^= jl_stack_chk_guard[sizeof(void*)-1]; \ + \ + b ^= jl_stack_chk_guard[sizeof(void*)-2]; \ + jl_stack_chk_guard[sizeof(void*)-2] ^= b; \ + b ^= jl_stack_chk_guard[sizeof(void*)-2]; \ + \ + c ^= jl_stack_chk_guard[0]; \ + jl_stack_chk_guard[0] ^= c; \ + c ^= jl_stack_chk_guard[0]; \ + } while (0) + #ifdef __cplusplus } #endif diff --git a/src/julia_internal.h b/src/julia_internal.h index 9df408979c9be..25a44e873680a 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -81,12 +81,16 @@ extern size_t jl_arr_xtralloc_limit; void jl_init_types(void); void jl_init_box_caches(void); -DLLEXPORT void jl_init_frontend(void); +void jl_init_frontend(void); void jl_init_primitives(void); void jl_init_codegen(void); void jl_init_intrinsic_functions(void); void jl_init_tasks(void *stack, size_t ssize); void jl_init_serializer(void); +void _julia_init(JL_IMAGE_SEARCH rel); +#ifdef COPY_STACKS +extern void *jl_stackbase; +#endif void jl_dump_bitcode(char *fname); void jl_dump_objfile(char *fname, int jit_model); @@ -141,6 +145,8 @@ extern uv_lib_t *jl_crtdll_handle; extern uv_lib_t *jl_winsock_handle; #endif +DLLEXPORT void jl_atexit_hook(); + #ifdef __cplusplus } #endif diff --git a/src/support/dirpath.h b/src/support/dirpath.h index 6cfd965ea0ea8..e906ddd7521b2 100644 --- a/src/support/dirpath.h +++ b/src/support/dirpath.h @@ -2,18 +2,17 @@ #define DIRPATH_H #ifdef _OS_WINDOWS_ -#define PATHSEP '\\' #define PATHSEPSTRING "\\" -#define PATHLISTSEP ';' #define PATHLISTSEPSTRING ";" -#define ISPATHSEP(c) ((c)=='/' || (c)=='\\') -#define MAXPATHLEN 1024 +#ifdef _MSC_VER +#define PATH_MAX MAX_PATH +#endif #else -#define PATHSEP '/' #define PATHSEPSTRING "/" -#define PATHLISTSEP ':' #define PATHLISTSEPSTRING ":" -#define ISPATHSEP(c) ((c)=='/') +#ifndef PATH_MAX // many platforms don't have a max path, we define one anyways +#define PATH_MAX 1024 +#endif #endif #endif diff --git a/src/support/ios.c b/src/support/ios.c index dbab4edbca747..d8bd66c7ede4f 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -836,7 +836,7 @@ static void _ios_init(ios_t *s) /* stream object initializers. we do no allocation. */ -ios_t *ios_file(ios_t *s, char *fname, int rd, int wr, int create, int trunc) +ios_t *ios_file(ios_t *s, const char *fname, int rd, int wr, int create, int trunc) { int flags; int fd; diff --git a/src/support/ios.h b/src/support/ios.h index d5a72fcc79da5..78c6518df92f1 100644 --- a/src/support/ios.h +++ b/src/support/ios.h @@ -100,7 +100,7 @@ DLLEXPORT size_t ios_readprep(ios_t *from, size_t n); /* stream creation */ DLLEXPORT -ios_t *ios_file(ios_t *s, char *fname, int rd, int wr, int create, int trunc); +ios_t *ios_file(ios_t *s, const char *fname, int rd, int wr, int create, int trunc); DLLEXPORT ios_t *ios_mem(ios_t *s, size_t initsize); ios_t *ios_str(ios_t *s, char *str); ios_t *ios_static_buffer(ios_t *s, char *buf, size_t sz); diff --git a/src/task.c b/src/task.c index 4fc7a0764d088..d15e355a897a2 100644 --- a/src/task.c +++ b/src/task.c @@ -154,18 +154,24 @@ jl_value_t *jl_exception_in_transit; jl_gcframe_t *jl_pgcstack = NULL; #endif -static void start_task(jl_task_t *t); - #ifdef COPY_STACKS -jl_jmp_buf * volatile jl_jmp_target; -DLLEXPORT jl_jmp_buf jl_base_ctx; +static jl_jmp_buf * volatile jl_jmp_target; + +#if defined(_CPU_X86_64_) || defined(_CPU_X86_) +#define ASM_COPY_STACKS +#endif +void *jl_stackbase; + +#ifndef ASM_COPY_STACKS +static jl_jmp_buf jl_base_ctx; // base context of stack +#endif static void save_stack(jl_task_t *t) { if (t->state == done_sym || t->state == failed_sym) return; volatile int _x; - size_t nb = (char*)t->stackbase - (char*)&_x; + size_t nb = (char*)jl_stackbase - (char*)&_x; char *buf; if (t->stkbuf == NULL || t->bufsz < nb) { buf = (char*)allocb(nb); @@ -180,12 +186,13 @@ static void save_stack(jl_task_t *t) } #if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) -void __declspec(noinline) restore_stack(jl_task_t *t, jl_jmp_buf *where, char *p) +void __declspec(noinline) #else -void __attribute__((noinline)) restore_stack(jl_task_t *t, jl_jmp_buf *where, char *p) +void __attribute__((noinline)) #endif +restore_stack(jl_task_t *t, jl_jmp_buf *where, char *p) { - char *_x = (char*)t->stackbase - t->ssize; + char *_x = (char*)jl_stackbase - t->ssize; if (!p) { p = _x; if ((char*)&_x > _x) { @@ -202,6 +209,91 @@ void __attribute__((noinline)) restore_stack(jl_task_t *t, jl_jmp_buf *where, ch } #endif +static jl_function_t *task_done_hook_func=NULL; + +static void NORETURN finish_task(jl_task_t *t, jl_value_t *resultval) +{ + if (t->exception != jl_nothing) + t->state = failed_sym; + else + t->state = done_sym; + t->result = resultval; + // TODO: early free of t->stkbuf +#ifdef COPY_STACKS + t->stkbuf = NULL; +#endif + if (task_done_hook_func == NULL) { + task_done_hook_func = (jl_function_t*)jl_get_global(jl_base_module, + jl_symbol("task_done_hook")); + } + if (task_done_hook_func != NULL) { + jl_apply(task_done_hook_func, (jl_value_t**)&t, 1); + } + abort(); +} + +#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) +static void __declspec(noinline) +#else +static void __attribute__((noinline)) +#endif +NORETURN start_task() +{ + // this runs the first time we switch to a task + jl_task_t *t = jl_current_task; + jl_value_t *res; + res = jl_apply(t->start, NULL, 0); + finish_task(t, res); + abort(); +} + +#ifndef ASM_COPY_STACKS +#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) +static void __declspec(noinline) +#else +static void __attribute__((noinline)) +#endif +set_base_ctx(char *__stk) +{ + if (jl_setjmp(jl_base_ctx, 1)) { + start_task(); + } +} +#else +void set_base_ctx(char *__stk) { } +#endif + + +DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) +{ // keep this function small, since we want to keep the stack frame + // leading up to this also quite small + _julia_init(rel); +#ifdef COPY_STACKS + char __stk; + jl_stackbase = (char*)(((uptrint_t)&__stk + sizeof(__stk))&-16); // also ensures stackbase is 16-byte aligned + set_base_ctx(&__stk); // separate function, to record the size of a stack frame +#endif +} + +#ifndef COPY_STACKS +static void init_task(jl_task_t *t) +{ + if (jl_setjmp(t->ctx, 0)) { + start_task(); + } + // this runs when the task is created + ptrint_t local_sp = (ptrint_t)&t; + ptrint_t new_sp = (ptrint_t)t->stack + t->ssize - _frame_offset; +#ifdef _P64 + // SP must be 16-byte aligned + new_sp = new_sp&-16; + local_sp = local_sp&-16; +#endif + memcpy((void*)new_sp, (void*)local_sp, _frame_offset); + rebase_state(&t->ctx, local_sp, new_sp); +} +#endif + static void ctx_switch(jl_task_t *t, jl_jmp_buf *where) { if (t == jl_current_task) @@ -248,7 +340,31 @@ static void ctx_switch(jl_task_t *t, jl_jmp_buf *where) if (t->stkbuf) { restore_stack(t, where, NULL); } else { +#ifdef ASM_COPY_STACKS + void *stackbase = jl_stackbase - 0x10; +#ifdef _CPU_X86_64_ +#ifdef _OS_WINDOWS_ + stackbase -= 0x20; +#endif + asm(" movq %0, %%rsp;\n" + " xorq %%rbp, %%rbp;\n" + " push %%rbp;\n" // instead of RSP + " jmp %P1;\n" // call stack_task with fake stack frame + " ud2" + : : "r"(stackbase), "i"(start_task) : "memory" ); +#elif defined(_CPU_X86_) + asm(" movl %0, %%esp;\n" + " xorl %%ebp, %%ebp;\n" + " push %%ebp;\n" // instead of ESP + " jmp %P1;\n" // call stack_task with fake stack frame + " ud2" + : : "r"(stackbase), "i"(start_task) : "memory" ); +#else +#error ASM_COPY_STACKS not supported on this cpu architecture +#endif +#else // ASM_COPY_STACKS jl_longjmp(jl_base_ctx, 1); +#endif } #else jl_longjmp(*where, 1); @@ -368,74 +484,6 @@ jl_value_t *jl_switchto(jl_task_t *t, jl_value_t *arg) return switchto(t); } -static jl_function_t *task_done_hook_func=NULL; - -static void finish_task(jl_task_t *t, jl_value_t *resultval) -{ - if (t->exception != jl_nothing) - t->state = failed_sym; - else - t->state = done_sym; - t->result = resultval; - // TODO: early free of t->stkbuf -#ifdef COPY_STACKS - t->stkbuf = NULL; -#endif - if (task_done_hook_func == NULL) { - task_done_hook_func = (jl_function_t*)jl_get_global(jl_base_module, - jl_symbol("task_done_hook")); - } - if (task_done_hook_func != NULL) { - jl_apply(task_done_hook_func, (jl_value_t**)&t, 1); - } - assert(0); -} - -static void start_task(jl_task_t *t) -{ - // this runs the first time we switch to t - jl_value_t *arg = jl_task_arg_in_transit; - jl_value_t *res; - JL_GC_PUSH1(&arg); - -#ifdef COPY_STACKS - ptrint_t local_sp = (ptrint_t)jl_pgcstack; - // here we attempt to figure out how big our stack frame is, since we - // might need to copy all of it later. this is a bit of a fuzzy guess. - local_sp += sizeof(jl_gcframe_t); - local_sp += 12*sizeof(void*); - t->stackbase = (void*)(local_sp + _frame_offset); -#endif - res = jl_apply(t->start, NULL, 0); - JL_GC_POP(); - finish_task(t, res); - assert(0); -} - -DLLEXPORT void jl_handle_stack_start() -{ - start_task(jl_current_task); -} - -#ifndef COPY_STACKS -static void init_task(jl_task_t *t) -{ - if (jl_setjmp(t->ctx, 0)) { - start_task(t); - } - // this runs when the task is created - ptrint_t local_sp = (ptrint_t)&t; - ptrint_t new_sp = (ptrint_t)t->stack + t->ssize - _frame_offset; -#ifdef _P64 - // SP must be 16-byte aligned - new_sp = new_sp&-16; - local_sp = local_sp&-16; -#endif - memcpy((void*)new_sp, (void*)local_sp, _frame_offset); - rebase_state(&t->ctx, local_sp, new_sp); -} -#endif - ptrint_t bt_data[MAX_BT_SIZE+1]; size_t bt_size = 0; @@ -581,9 +629,8 @@ DLLEXPORT size_t rec_backtrace_ctx(ptrint_t *data, size_t maxsize, unw_context_t do { if (n >= maxsize) break; - if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) { + if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) break; - } data[n++] = ip; } while (unw_step(&cursor) > 0); return n; @@ -602,9 +649,8 @@ size_t rec_backtrace_ctx_dwarf(ptrint_t *data, size_t maxsize, unw_context_t *uc do { if (n >= maxsize) break; - if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) { + if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) break; - } data[n++] = ip; } while (unw_step(&cursor) > 0); return n; @@ -849,7 +895,6 @@ void jl_init_tasks(void *stack, size_t ssize) jl_current_task = (jl_task_t*)allocobj(sizeof(jl_task_t)); jl_current_task->type = (jl_value_t*)jl_task_type; #ifdef COPY_STACKS - jl_current_task->stackbase = NULL; jl_current_task->ssize = 0; // size of saved piece jl_current_task->bufsz = 0; #else diff --git a/test/lineedit.jl b/test/lineedit.jl index c5cf284f12be4..b23a5fa9b07cf 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -21,28 +21,28 @@ const bar_keymap = Dict( 'b' => (o...)->(global b_bar; b_bar += 1) ) -test1_func = LineEdit.keymap([foo_keymap]) +test1_dict = LineEdit.keymap([foo_keymap]) -function run_test(f,buf) +function run_test(d,buf) global a_foo, a_bar, b_bar a_foo = a_bar = b_bar = 0 while !eof(buf) - f(buf,nothing) + LineEdit.match_input(d, nothing, buf)(nothing,nothing) end end -run_test(test1_func,IOBuffer("aa")) +run_test(test1_dict,IOBuffer("aa")) @test a_foo == 2 -test2_func = LineEdit.keymap([foo2_keymap, foo_keymap]) +test2_dict = LineEdit.keymap([foo2_keymap, foo_keymap]) -run_test(test2_func,IOBuffer("aaabb")) +run_test(test2_dict,IOBuffer("aaabb")) @test a_foo == 3 @test b_foo == 2 -test3_func = LineEdit.keymap([bar_keymap, foo_keymap]) +test3_dict = LineEdit.keymap([bar_keymap, foo_keymap]) -run_test(test3_func,IOBuffer("aab")) +run_test(test3_dict,IOBuffer("aab")) @test a_bar == 2 @test b_bar == 1 diff --git a/test/random.jl b/test/random.jl index 221118dcfded3..a59d0edeca2b6 100644 --- a/test/random.jl +++ b/test/random.jl @@ -95,20 +95,6 @@ for T in [UInt32, UInt64, UInt128, Int128] @test rand(s) == r end -randn() -randn(100000) -randn!(Array(Float64, 100000)) -randn(MersenneTwister(10)) -randn(MersenneTwister(10), 100000) -randn!(MersenneTwister(10), Array(Float64, 100000)) - -randexp() -randexp(100000) -randexp!(Array(Float64, 100000)) -randexp(MersenneTwister(10)) -randexp(MersenneTwister(10), 100000) -randexp!(MersenneTwister(10), Array(Float64, 100000)) - # Test ziggurat tables ziggurat_table_size = 256 nmantissa = int64(2)^51 # one bit for the sign @@ -278,14 +264,38 @@ let mt = MersenneTwister() end end -# test rand! API: rand!([rng], A, [coll]) -let mt = MersenneTwister(0) - for T in [Base.IntTypes..., Float16, Float32, Float64] - for A in (Array(T, 5), Array(T, 2, 2)) - rand!(A) - rand!(mt, A) - rand!(A, T[1,2,3]) - rand!(mt, A, T[1,2,3]) +# test all rand APIs +for rng in ([], [MersenneTwister()], [RandomDevice()]) + for f in [rand, randn, randexp] + f(rng...) ::Float64 + f(rng..., 5) ::Vector{Float64} + f(rng..., 2, 3) ::Array{Float64, 2} + end + for f! in [randn!, randexp!] + f!(rng..., Array(Float64, 5)) ::Vector{Float64} + f!(rng..., Array(Float64, 2, 3)) ::Array{Float64, 2} + end + + randbool(rng...) ::Bool + randbool(rng..., 5) ::BitArray{1} + randbool(rng..., 2, 3) ::BitArray{2} + rand!(rng..., BitArray(5)) ::BitArray{1} + rand!(rng..., BitArray(2, 3)) ::BitArray{2} + + for T in [Base.IntTypes..., Bool, Float16, Float32, Float64] + a0 = rand(rng..., T) ::T + a1 = rand(rng..., T, 5) ::Vector{T} + a2 = rand(rng..., T, 2, 3) ::Array{T, 2} + if T <: FloatingPoint + for a in [a0, a1..., a2...] + @test 0.0 <= a < 1.0 + end + end + for A in (Array(T, 5), Array(T, 2, 3)) + rand!(rng..., A) ::typeof(A) + rand!(rng..., A, T[0,1,2]) ::typeof(A) + rand!(rng..., sparse(A)) ::typeof(sparse(A)) + rand!(rng..., sparse(A), T[0,1,2]) ::typeof(sparse(A)) end end end diff --git a/ui/Makefile b/ui/Makefile index 181e79fa9ad75..d877f1027c09e 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -58,7 +58,6 @@ julia_res.o: $(JULIAHOME)/contrib/windows/julia.rc $(CROSS_COMPILE)windres $< -O coff -o $@ -DJLVER=$$JLVERi -DJLVER_STR=\\\"$$JLVER\\\" $(build_bindir)/julia$(EXE): julia_res.o $(build_bindir)/julia-debug$(EXE): julia_res.o -JLDFLAGS += julia_res.o endif endif diff --git a/ui/repl.c b/ui/repl.c index 31634902aacbc..4be3f55e87643 100644 --- a/ui/repl.c +++ b/ui/repl.c @@ -4,7 +4,6 @@ */ #include -#include #include #include #include @@ -20,7 +19,6 @@ #ifndef _MSC_VER #include -#include #include #else #include "getopt.h" @@ -34,27 +32,15 @@ #error "JL_SYSTEM_IMAGE_PATH not defined!" #endif -#ifdef _MSC_VER -#define PATH_MAX MAX_PATH -#endif - #ifdef __cplusplus extern "C" { #endif -#ifdef _MSC_VER -DLLEXPORT char * dirname(char *); -#endif - -extern DLLEXPORT char *julia_home; - -char system_image[256] = JL_SYSTEM_IMAGE_PATH; - static int lisp_prompt = 0; static int codecov = JL_LOG_NONE; static int malloclog= JL_LOG_NONE; static char *program = NULL; -char *image_file = NULL; +static int imagepathspecified = 0; static const char *usage = "julia [options] [program] [args...]\n"; static const char *opts = @@ -67,7 +53,8 @@ static const char *opts = " -E, --print Evaluate and show \n" " -P, --post-boot Evaluate , but don't disable interactive mode\n" " -L, --load Load immediately on all processors\n" - " -J, --sysimage Start up with the given system image file\n\n" + " -J, --sysimage Start up with the given system image file\n" + " -C --cpu-target Limit usage of cpu features up to \n\n" " -p Run n local processes\n" " --machinefile Run processes on hosts listed in \n\n" @@ -91,15 +78,15 @@ static const char *opts = void parse_opts(int *argcp, char ***argvp) { - static char* shortopts = "+H:T:hJ:O"; + static char* shortopts = "+H:hJ:C:O"; static struct option longopts[] = { { "home", required_argument, 0, 'H' }, - { "tab", required_argument, 0, 'T' }, { "build", required_argument, 0, 'b' }, { "lisp", no_argument, &lisp_prompt, 1 }, { "help", no_argument, 0, 'h' }, { "sysimage", required_argument, 0, 'J' }, { "code-coverage", optional_argument, 0, 'c' }, + { "cpu-target", required_argument, 0, 'C' }, { "track-allocation",required_argument, 0, 'm' }, { "check-bounds", required_argument, 0, 300 }, { "optimize", no_argument, 0, 'O' }, @@ -110,8 +97,6 @@ void parse_opts(int *argcp, char ***argvp) }; int c; opterr = 0; - int imagepathspecified=0; - image_file = system_image; int skip = 0; int lastind = optind; while ((c = getopt_long(*argcp,*argvp,shortopts,longopts,0)) != -1) { @@ -123,19 +108,22 @@ void parse_opts(int *argcp, char ***argvp) lastind = optind; break; case 'H': - julia_home = strdup(optarg); + jl_compileropts.julia_home = strdup(optarg); break; case 'b': jl_compileropts.build_path = strdup(optarg); if (!imagepathspecified) - image_file = NULL; + jl_compileropts.image_file = NULL; break; case 'J': - image_file = strdup(optarg); + jl_compileropts.image_file = strdup(optarg); imagepathspecified = 1; break; + case 'C': + jl_compileropts.cpu_target = strdup(optarg); + break; case 'h': - printf("%s%s", usage, opts); + ios_printf(ios_stdout, "%s%s", usage, opts); exit(0); case 'O': jl_compileropts.opt_level = 1; @@ -206,47 +194,14 @@ void parse_opts(int *argcp, char ***argvp) } jl_compileropts.code_coverage = codecov; jl_compileropts.malloc_log = malloclog; - if (!julia_home) { - julia_home = getenv("JULIA_HOME"); - if (julia_home) { - julia_home = strdup(julia_home); - } - else { - char *julia_path = (char*)malloc(PATH_MAX); - size_t path_size = PATH_MAX; - uv_exepath(julia_path, &path_size); - julia_home = strdup(dirname(julia_path)); - free(julia_path); - } - } optind -= skip; *argvp += optind; *argcp -= optind; - if (image_file==NULL && *argcp > 0) { + if (jl_compileropts.image_file==NULL && *argcp > 0) { if (strcmp((*argvp)[0], "-")) { program = (*argvp)[0]; } } - if (image_file) { - if (image_file[0] != PATHSEP) { - uv_stat_t stbuf; - char path[512]; - if (!imagepathspecified) { - // build time path relative to JULIA_HOME - snprintf(path, sizeof(path), "%s%s%s", - julia_home, PATHSEPSTRING, system_image); - image_file = strdup(path); - } - else if (jl_stat(image_file, (char*)&stbuf) != 0) { - // otherwise try julia_home/../lib/julia/%s - snprintf(path, sizeof(path), "%s%s%s", - julia_home, - PATHSEPSTRING ".." PATHSEPSTRING "lib" PATHSEPSTRING "julia" PATHSEPSTRING, - image_file); - image_file = strdup(path); - } - } - } } static int exec_program(void) @@ -305,7 +260,7 @@ static void print_profile(void) } #endif -int true_main(int argc, char *argv[]) +static int true_main(int argc, char *argv[]) { if (jl_base_module != NULL) { jl_array_t *args = (jl_array_t*)jl_get_global(jl_base_module, jl_symbol("ARGS")); @@ -357,10 +312,11 @@ int true_main(int argc, char *argv[]) JL_PUTS("\n",JL_STDOUT); goto again; } - uv_tty_reset_mode(); return iserr; } +DLLEXPORT extern void julia_save(); + #ifndef _OS_WINDOWS_ int main(int argc, char *argv[]) { @@ -378,14 +334,20 @@ int wmain(int argc, wchar_t *argv[], wchar_t *envp[]) argv[i] = (wchar_t*)arg; } #endif + char a=255,b='\n',c=0; + SWAP_STACK_CHK_GUARD(a,b,c); libsupport_init(); parse_opts(&argc, (char***)&argv); if (lisp_prompt) { jl_lisp_prompt(); return 0; } - julia_init(lisp_prompt ? NULL : image_file); - return julia_trampoline(argc, (char**)argv, true_main); + julia_init(imagepathspecified ? JL_IMAGE_CWD : JL_IMAGE_JULIA_HOME); + int ret = true_main(argc, (char**)argv); + jl_atexit_hook(); + julia_save(); + SWAP_STACK_CHK_GUARD(a,b,c); + return ret; } #ifdef __cplusplus