oso-oso 0.23.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +7 -6
- data/Makefile +1 -1
- data/ext/oso-oso/lib/libpolar.dylib +0 -0
- data/ext/oso-oso/lib/libpolar.so +0 -0
- data/ext/oso-oso/lib/polar.dll +0 -0
- data/lib/oso/oso.rb +5 -33
- data/lib/oso/polar/data/adapter/active_record_adapter.rb +57 -0
- data/lib/oso/polar/data/adapter.rb +22 -0
- data/lib/oso/polar/data/filter.rb +59 -0
- data/lib/oso/polar/data.rb +4 -0
- data/lib/oso/polar/data_filtering.rb +0 -190
- data/lib/oso/polar/errors.rb +0 -1
- data/lib/oso/polar/expression.rb +1 -1
- data/lib/oso/polar/ffi/error.rb +4 -22
- data/lib/oso/polar/ffi/polar.rb +60 -35
- data/lib/oso/polar/ffi/query.rb +48 -22
- data/lib/oso/polar/ffi/{source.rb → rust_string.rb} +9 -3
- data/lib/oso/polar/ffi.rb +28 -20
- data/lib/oso/polar/host.rb +35 -10
- data/lib/oso/polar/polar.rb +76 -16
- data/lib/oso/polar/query.rb +19 -10
- data/lib/oso/polar.rb +1 -0
- data/lib/oso/version.rb +1 -1
- data/lib/oso.rb +0 -1
- metadata +7 -5
- data/lib/oso/polar/ffi/message.rb +0 -40
- data/lib/oso/polar/ffi/query_event.rb +0 -23
data/lib/oso/polar/ffi/polar.rb
CHANGED
@@ -6,7 +6,7 @@ module Oso
|
|
6
6
|
module Polar
|
7
7
|
module FFI
|
8
8
|
# Wrapper class for Polar FFI pointer + operations.
|
9
|
-
class Polar < ::FFI::AutoPointer
|
9
|
+
class Polar < ::FFI::AutoPointer # rubocop:disable Metrics/ClassLength
|
10
10
|
attr_accessor :enrich_message
|
11
11
|
|
12
12
|
Rust = Module.new do
|
@@ -14,21 +14,28 @@ module Oso
|
|
14
14
|
ffi_lib FFI::LIB_PATH
|
15
15
|
|
16
16
|
attach_function :new, :polar_new, [], FFI::Polar
|
17
|
-
attach_function :load, :polar_load, [FFI::Polar, :string],
|
18
|
-
attach_function :clear_rules, :polar_clear_rules, [FFI::Polar],
|
17
|
+
attach_function :load, :polar_load, [FFI::Polar, :string], CResultVoid
|
18
|
+
attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], CResultVoid
|
19
19
|
attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
|
20
20
|
attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
|
21
|
-
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32],
|
22
|
-
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32],
|
23
|
-
attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string],
|
24
|
-
attach_function :register_mro, :polar_register_mro, [FFI::Polar, :string, :string],
|
25
|
-
attach_function :next_message, :polar_next_polar_message, [FFI::Polar],
|
21
|
+
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], CResultQuery
|
22
|
+
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32], CResultQuery
|
23
|
+
attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string], CResultVoid
|
24
|
+
attach_function :register_mro, :polar_register_mro, [FFI::Polar, :string, :string], CResultVoid
|
25
|
+
attach_function :next_message, :polar_next_polar_message, [FFI::Polar], CResultString
|
26
26
|
attach_function :free, :polar_free, [FFI::Polar], :int32
|
27
|
+
attach_function :result_free, :result_free, [:pointer], :int32
|
27
28
|
attach_function(
|
28
29
|
:build_filter_plan,
|
29
30
|
:polar_build_filter_plan,
|
30
31
|
[FFI::Polar, :string, :string, :string, :string],
|
31
|
-
|
32
|
+
CResultString
|
33
|
+
)
|
34
|
+
attach_function(
|
35
|
+
:build_data_filter,
|
36
|
+
:polar_build_data_filter,
|
37
|
+
[FFI::Polar, :string, :string, :string, :string],
|
38
|
+
CResultString
|
32
39
|
)
|
33
40
|
end
|
34
41
|
private_constant :Rust
|
@@ -36,10 +43,7 @@ module Oso
|
|
36
43
|
# @return [FFI::Polar]
|
37
44
|
# @raise [FFI::Error] if the FFI call returns an error.
|
38
45
|
def self.create
|
39
|
-
|
40
|
-
handle_error if polar.null?
|
41
|
-
|
42
|
-
polar
|
46
|
+
Rust.new
|
43
47
|
end
|
44
48
|
|
45
49
|
def build_filter_plan(types, partials, variable, class_tag)
|
@@ -47,9 +51,19 @@ module Oso
|
|
47
51
|
partials = JSON.dump(partials)
|
48
52
|
plan = Rust.build_filter_plan(self, types, partials, variable, class_tag)
|
49
53
|
process_messages
|
50
|
-
|
54
|
+
plan = check_result plan
|
55
|
+
# TODO(gw) more error checking?
|
56
|
+
JSON.parse plan.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_data_filter(types, partials, variable, class_tag)
|
60
|
+
types = JSON.dump(types)
|
61
|
+
partials = JSON.dump(partials)
|
62
|
+
plan = Rust.build_data_filter(self, types, partials, variable, class_tag)
|
63
|
+
process_messages
|
64
|
+
plan = check_result plan
|
51
65
|
# TODO(gw) more error checking?
|
52
|
-
JSON.parse plan
|
66
|
+
JSON.parse plan.to_s
|
53
67
|
end
|
54
68
|
|
55
69
|
# @param sources [Array<Source>]
|
@@ -57,14 +71,14 @@ module Oso
|
|
57
71
|
def load(sources)
|
58
72
|
loaded = Rust.load(self, JSON.dump(sources))
|
59
73
|
process_messages
|
60
|
-
|
74
|
+
check_result loaded
|
61
75
|
end
|
62
76
|
|
63
77
|
# @raise [FFI::Error] if the FFI call returns an error.
|
64
78
|
def clear_rules
|
65
79
|
cleared = Rust.clear_rules(self)
|
66
80
|
process_messages
|
67
|
-
|
81
|
+
check_result cleared
|
68
82
|
end
|
69
83
|
|
70
84
|
# @return [FFI::Query] if there are remaining inline queries.
|
@@ -79,12 +93,7 @@ module Oso
|
|
79
93
|
# @return [Integer]
|
80
94
|
# @raise [FFI::Error] if the FFI call returns an error.
|
81
95
|
def new_id
|
82
|
-
|
83
|
-
# TODO(gj): I don't think this error check is correct. If getting a new ID fails on the
|
84
|
-
# Rust side, it'll probably surface as a panic (e.g., the KB lock is poisoned).
|
85
|
-
handle_error if id.zero?
|
86
|
-
|
87
|
-
id
|
96
|
+
Rust.new_id(self)
|
88
97
|
end
|
89
98
|
|
90
99
|
# @param str [String] Query string.
|
@@ -93,9 +102,7 @@ module Oso
|
|
93
102
|
def new_query_from_str(str)
|
94
103
|
query = Rust.new_query_from_str(self, str, 0)
|
95
104
|
process_messages
|
96
|
-
|
97
|
-
|
98
|
-
query
|
105
|
+
check_result query
|
99
106
|
end
|
100
107
|
|
101
108
|
# @param term [Hash<String, Object>]
|
@@ -104,9 +111,7 @@ module Oso
|
|
104
111
|
def new_query_from_term(term)
|
105
112
|
query = Rust.new_query_from_term(self, JSON.dump(term), 0)
|
106
113
|
process_messages
|
107
|
-
|
108
|
-
|
109
|
-
query
|
114
|
+
check_result query
|
110
115
|
end
|
111
116
|
|
112
117
|
# @param name [String]
|
@@ -114,7 +119,7 @@ module Oso
|
|
114
119
|
# @raise [FFI::Error] if the FFI call returns an error.
|
115
120
|
def register_constant(value, name:)
|
116
121
|
registered = Rust.register_constant(self, name, JSON.dump(value))
|
117
|
-
|
122
|
+
check_result registered
|
118
123
|
end
|
119
124
|
|
120
125
|
# @param name [String]
|
@@ -122,11 +127,24 @@ module Oso
|
|
122
127
|
# @raise [FFI::Error] if the FFI call returns an error.
|
123
128
|
def register_mro(name, mro)
|
124
129
|
registered = Rust.register_mro(self, name, JSON.dump(mro))
|
125
|
-
|
130
|
+
check_result registered
|
126
131
|
end
|
127
132
|
|
128
133
|
def next_message
|
129
|
-
Rust.next_message(self)
|
134
|
+
check_result Rust.next_message(self)
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_message(message, enrich_message)
|
138
|
+
message = JSON.parse(message.to_s)
|
139
|
+
kind = message['kind']
|
140
|
+
msg = enrich_message.call(message['msg'])
|
141
|
+
|
142
|
+
case kind
|
143
|
+
when 'Print'
|
144
|
+
puts(msg)
|
145
|
+
when 'Warning'
|
146
|
+
warn(format('[warning] %<msg>s', msg: msg))
|
147
|
+
end
|
130
148
|
end
|
131
149
|
|
132
150
|
def process_messages
|
@@ -134,12 +152,19 @@ module Oso
|
|
134
152
|
message = next_message
|
135
153
|
break if message.null?
|
136
154
|
|
137
|
-
message
|
155
|
+
process_message(message, enrich_message)
|
138
156
|
end
|
139
157
|
end
|
140
158
|
|
141
|
-
def
|
142
|
-
|
159
|
+
def check_result(res)
|
160
|
+
result = res[:result]
|
161
|
+
error = res[:error]
|
162
|
+
Rust.result_free(res)
|
163
|
+
|
164
|
+
raise 'internal error: both result and error pointers are not null' if !error.null? && !result.zero?
|
165
|
+
raise FFI::Error.get(error, enrich_message) unless error.null?
|
166
|
+
|
167
|
+
result
|
143
168
|
end
|
144
169
|
end
|
145
170
|
end
|
data/lib/oso/polar/ffi/query.rb
CHANGED
@@ -13,15 +13,16 @@ module Oso
|
|
13
13
|
extend ::FFI::Library
|
14
14
|
ffi_lib FFI::LIB_PATH
|
15
15
|
|
16
|
-
attach_function :debug_command, :polar_debug_command, [FFI::Query, :string],
|
17
|
-
attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string],
|
18
|
-
attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32],
|
19
|
-
attach_function :application_error, :polar_application_error, [FFI::Query, :string],
|
20
|
-
attach_function :next_event, :polar_next_query_event, [FFI::Query],
|
21
|
-
attach_function :next_message, :polar_next_query_message, [FFI::Query],
|
22
|
-
attach_function :source, :polar_query_source_info, [FFI::Query],
|
16
|
+
attach_function :debug_command, :polar_debug_command, [FFI::Query, :string], CResultVoid
|
17
|
+
attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string], CResultVoid
|
18
|
+
attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32], CResultVoid
|
19
|
+
attach_function :application_error, :polar_application_error, [FFI::Query, :string], CResultVoid
|
20
|
+
attach_function :next_event, :polar_next_query_event, [FFI::Query], CResultString
|
21
|
+
attach_function :next_message, :polar_next_query_message, [FFI::Query], CResultString
|
22
|
+
attach_function :source, :polar_query_source_info, [FFI::Query], CResultString
|
23
23
|
attach_function :free, :query_free, [FFI::Query], :int32
|
24
|
-
attach_function :
|
24
|
+
attach_function :result_free, :result_free, [:pointer], :int32
|
25
|
+
attach_function :bind, :polar_bind, [FFI::Query, :string, :string], CResultVoid
|
25
26
|
end
|
26
27
|
private_constant :Rust
|
27
28
|
|
@@ -30,15 +31,15 @@ module Oso
|
|
30
31
|
def debug_command(cmd)
|
31
32
|
res = Rust.debug_command(self, cmd)
|
32
33
|
process_messages
|
33
|
-
|
34
|
+
check_result res
|
34
35
|
end
|
35
36
|
|
36
|
-
# @param
|
37
|
+
# @param value [Object]
|
37
38
|
# @param call_id [Integer]
|
38
39
|
# @raise [FFI::Error] if the FFI call returns an error.
|
39
|
-
def call_result(
|
40
|
-
res = Rust.call_result(self, call_id,
|
41
|
-
|
40
|
+
def call_result(value, call_id:)
|
41
|
+
res = Rust.call_result(self, call_id, JSON.dump(value))
|
42
|
+
check_result res
|
42
43
|
end
|
43
44
|
|
44
45
|
# @param result [Boolean]
|
@@ -47,14 +48,14 @@ module Oso
|
|
47
48
|
def question_result(result, call_id:)
|
48
49
|
result = result ? 1 : 0
|
49
50
|
res = Rust.question_result(self, call_id, result)
|
50
|
-
|
51
|
+
check_result res
|
51
52
|
end
|
52
53
|
|
53
54
|
# @param message [String]
|
54
55
|
# @raise [FFI::Error] if the FFI call returns an error.
|
55
56
|
def application_error(message)
|
56
57
|
res = Rust.application_error(self, message)
|
57
|
-
|
58
|
+
check_result res
|
58
59
|
end
|
59
60
|
|
60
61
|
# @return [::Oso::Polar::QueryEvent]
|
@@ -62,18 +63,32 @@ module Oso
|
|
62
63
|
def next_event
|
63
64
|
event = Rust.next_event(self)
|
64
65
|
process_messages
|
65
|
-
|
66
|
+
event = check_result event
|
66
67
|
|
67
68
|
::Oso::Polar::QueryEvent.new(JSON.parse(event.to_s))
|
68
69
|
end
|
69
70
|
|
70
71
|
def bind(name, value)
|
71
72
|
res = Rust.bind(self, name, JSON.dump(value))
|
72
|
-
|
73
|
+
check_result res
|
73
74
|
end
|
74
75
|
|
75
76
|
def next_message
|
76
|
-
Rust.next_message(self)
|
77
|
+
check_result Rust.next_message(self)
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_message(message, enrich_message)
|
81
|
+
message = JSON.parse(message.to_s)
|
82
|
+
kind = message['kind']
|
83
|
+
msg = message['msg']
|
84
|
+
msg = enrich_message.call(msg)
|
85
|
+
|
86
|
+
case kind
|
87
|
+
when 'Print'
|
88
|
+
puts(msg)
|
89
|
+
when 'Warning'
|
90
|
+
warn(format('[warning] %<msg>s', msg: msg))
|
91
|
+
end
|
77
92
|
end
|
78
93
|
|
79
94
|
def process_messages
|
@@ -81,7 +96,7 @@ module Oso
|
|
81
96
|
message = next_message
|
82
97
|
break if message.null?
|
83
98
|
|
84
|
-
message
|
99
|
+
process_message(message, enrich_message)
|
85
100
|
end
|
86
101
|
end
|
87
102
|
|
@@ -89,13 +104,24 @@ module Oso
|
|
89
104
|
# @raise [FFI::Error] if the FFI call returns an error.
|
90
105
|
def source
|
91
106
|
res = Rust.source(self)
|
92
|
-
|
107
|
+
res = check_result res
|
93
108
|
|
94
109
|
res.to_s
|
95
110
|
end
|
96
111
|
|
97
|
-
|
98
|
-
|
112
|
+
# Unwrap the result by (a) extracting the pointers for
|
113
|
+
# result and error, (b) freeing the result pointers, and then
|
114
|
+
# (c) either returning the result pointer, or constructing and
|
115
|
+
# raising the error.
|
116
|
+
def check_result(res)
|
117
|
+
result = res[:result]
|
118
|
+
error = res[:error]
|
119
|
+
Rust.result_free(res)
|
120
|
+
|
121
|
+
raise 'internal error: both result and error pointers are not null' if !error.null? && !result.zero?
|
122
|
+
raise FFI::Error.get(error, enrich_message) unless error.null?
|
123
|
+
|
124
|
+
result
|
99
125
|
end
|
100
126
|
end
|
101
127
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
module FFI
|
6
|
-
# Wrapper class for
|
7
|
-
|
8
|
+
# Wrapper class for Rust strings.
|
9
|
+
#
|
10
|
+
# Since we force all strings to go through this
|
11
|
+
# the `AutoPointer` class will handle
|
12
|
+
# actually freeing the string when deleting it
|
13
|
+
class RustString < ::FFI::AutoPointer
|
8
14
|
# @return [String]
|
9
15
|
def to_s
|
10
16
|
@to_s ||= read_string.force_encoding('UTF-8')
|
@@ -14,7 +20,7 @@ module Oso
|
|
14
20
|
extend ::FFI::Library
|
15
21
|
ffi_lib FFI::LIB_PATH
|
16
22
|
|
17
|
-
attach_function :free, :string_free, [
|
23
|
+
attach_function :free, :string_free, [RustString], :int32
|
18
24
|
end
|
19
25
|
|
20
26
|
private_constant :Rust
|
data/lib/oso/polar/ffi.rb
CHANGED
@@ -4,6 +4,7 @@ require 'ffi'
|
|
4
4
|
|
5
5
|
module Oso
|
6
6
|
module Polar
|
7
|
+
# FFI classes shared between all ffi/*.rb modules
|
7
8
|
module FFI
|
8
9
|
LIB = "#{::FFI::Platform::LIBPREFIX}polar.#{::FFI::Platform::LIBSUFFIX}"
|
9
10
|
RELEASE_PATH = File.expand_path(File.join(__dir__, "../../../ext/oso-oso/lib/#{LIB}"))
|
@@ -18,41 +19,50 @@ module Oso
|
|
18
19
|
|
19
20
|
# Wrapper class for Polar FFI pointer + operations.
|
20
21
|
class Polar < ::FFI::AutoPointer
|
22
|
+
def zero?
|
23
|
+
null?
|
24
|
+
end
|
25
|
+
|
21
26
|
def self.release(ptr)
|
22
27
|
Rust.free(ptr) unless ptr.null?
|
23
28
|
end
|
24
29
|
end
|
25
30
|
# Wrapper class for Query FFI pointer + operations.
|
26
31
|
class Query < ::FFI::AutoPointer
|
27
|
-
def
|
28
|
-
|
32
|
+
def zero?
|
33
|
+
null?
|
29
34
|
end
|
30
|
-
|
31
|
-
# Wrapper class for QueryEvent FFI pointer + operations.
|
32
|
-
class QueryEvent < ::FFI::AutoPointer
|
35
|
+
|
33
36
|
def self.release(ptr)
|
34
37
|
Rust.free(ptr) unless ptr.null?
|
35
38
|
end
|
36
39
|
end
|
37
|
-
|
38
|
-
class
|
39
|
-
|
40
|
-
|
40
|
+
|
41
|
+
# Wrapper class for Rust strings FFI pointer + operations.
|
42
|
+
class RustString < ::FFI::AutoPointer
|
43
|
+
def zero?
|
44
|
+
null?
|
41
45
|
end
|
42
|
-
|
43
|
-
# Wrapper class for Message FFI pointer + operations.
|
44
|
-
class Message < ::FFI::AutoPointer
|
46
|
+
|
45
47
|
def self.release(ptr)
|
46
48
|
Rust.free(ptr) unless ptr.null?
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
# Helper method to generate a Result type for different
|
53
|
+
# inner types
|
54
|
+
def self.result(result_klass)
|
55
|
+
Class.new(::FFI::Struct) do
|
56
|
+
layout :result, result_klass, :error, RustString
|
57
|
+
end.by_ref
|
55
58
|
end
|
59
|
+
|
60
|
+
# Defines the result type version of
|
61
|
+
# each of these structs
|
62
|
+
# result(T) => { result: T, error: string }
|
63
|
+
CResultVoid = result(:int)
|
64
|
+
CResultString = result(RustString)
|
65
|
+
CResultQuery = result(Query)
|
56
66
|
end
|
57
67
|
private_constant :FFI
|
58
68
|
end
|
@@ -60,7 +70,5 @@ end
|
|
60
70
|
|
61
71
|
require 'oso/polar/ffi/polar'
|
62
72
|
require 'oso/polar/ffi/query'
|
63
|
-
require 'oso/polar/ffi/query_event'
|
64
73
|
require 'oso/polar/ffi/error'
|
65
|
-
require 'oso/polar/ffi/
|
66
|
-
require 'oso/polar/ffi/source'
|
74
|
+
require 'oso/polar/ffi/rust_string'
|
data/lib/oso/polar/host.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'data'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
# Ruby code reloaders (i.e. the one used by rails) swap out the value of
|
@@ -67,7 +69,7 @@ module Oso
|
|
67
69
|
public
|
68
70
|
|
69
71
|
attr_writer :accept_expression
|
70
|
-
attr_accessor :build_query, :combine_query, :exec_query
|
72
|
+
attr_accessor :build_query, :combine_query, :exec_query, :adapter
|
71
73
|
|
72
74
|
DEFAULT_COMBINE_QUERY = proc { raise 'implement combine_query to use data filtering' }
|
73
75
|
DEFAULT_BUILD_QUERY = proc { raise 'implement build_query to use data filtering' }
|
@@ -95,9 +97,10 @@ module Oso
|
|
95
97
|
# @return [Class]
|
96
98
|
# @raise [UnregisteredClassError] if the class has not been registered.
|
97
99
|
def get_class(name)
|
98
|
-
|
100
|
+
typ = types[name]
|
101
|
+
raise UnregisteredClassError, name if typ.nil?
|
99
102
|
|
100
|
-
|
103
|
+
typ.klass.get
|
101
104
|
end
|
102
105
|
|
103
106
|
# Store a Ruby class in the {#types} cache.
|
@@ -316,8 +319,26 @@ module Oso
|
|
316
319
|
end
|
317
320
|
else
|
318
321
|
instance_id = nil
|
319
|
-
|
320
|
-
|
322
|
+
class_id = nil
|
323
|
+
class_repr = nil
|
324
|
+
# id=class_id,
|
325
|
+
|
326
|
+
# pass `class_id` & `class_repr` for registered types
|
327
|
+
if value.is_a?(Class) && types.key?(value)
|
328
|
+
instance_id = class_id = types[value].id
|
329
|
+
elsif types.key?(value.class)
|
330
|
+
class_id = types[value.class].id
|
331
|
+
class_repr = types[value.class].name
|
332
|
+
end
|
333
|
+
|
334
|
+
{
|
335
|
+
'ExternalInstance' => {
|
336
|
+
'instance_id' => cache_instance(value, id: instance_id),
|
337
|
+
'repr' => nil,
|
338
|
+
'class_repr' => class_repr,
|
339
|
+
'class_id' => class_id
|
340
|
+
}
|
341
|
+
}
|
321
342
|
end
|
322
343
|
{ 'value' => value }
|
323
344
|
end
|
@@ -337,21 +358,25 @@ module Oso
|
|
337
358
|
value
|
338
359
|
when 'Number'
|
339
360
|
num = value.values.first
|
340
|
-
|
361
|
+
case value.keys.first
|
362
|
+
when 'Float'
|
341
363
|
case num
|
342
364
|
when 'Infinity'
|
343
|
-
|
365
|
+
Float::INFINITY
|
344
366
|
when '-Infinity'
|
345
|
-
|
367
|
+
-Float::INFINITY
|
346
368
|
when 'NaN'
|
347
|
-
|
369
|
+
Float::NAN
|
348
370
|
else
|
349
371
|
unless value['Float'].is_a? Float # rubocop:disable Metrics/BlockNesting
|
350
372
|
raise PolarRuntimeError, "Expected a floating point number, got \"#{value['Float']}\""
|
351
373
|
end
|
374
|
+
|
375
|
+
num
|
352
376
|
end
|
377
|
+
else
|
378
|
+
num
|
353
379
|
end
|
354
|
-
num
|
355
380
|
when 'List'
|
356
381
|
value.map { |el| to_ruby(el) }
|
357
382
|
when 'Dictionary'
|
data/lib/oso/polar/polar.rb
CHANGED
@@ -84,20 +84,8 @@ module Oso
|
|
84
84
|
@ffi_polar
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
def get_class_name(klass) # rubocop:disable Metrics/AbcSize
|
90
|
-
if host.types.key? klass
|
91
|
-
host.types[klass].name
|
92
|
-
elsif host.types.key? klass.name
|
93
|
-
host.types[klass.name].name
|
94
|
-
else
|
95
|
-
rec = host.types.values.find { |v| v.klass.get == klass }
|
96
|
-
raise "Unknown class `#{klass}`" if rec.nil?
|
97
|
-
|
98
|
-
host.types[klass] = rec
|
99
|
-
rec.name
|
100
|
-
end
|
87
|
+
def name_to_class(class_name)
|
88
|
+
host.types[class_name].klass.get
|
101
89
|
end
|
102
90
|
|
103
91
|
# Clear all rules and rule sources from the current Polar instance
|
@@ -229,6 +217,7 @@ module Oso
|
|
229
217
|
exec_query: exec_query || maybe_mtd(cls, :exec_query)
|
230
218
|
)
|
231
219
|
register_constant(cls, name: name)
|
220
|
+
host.register_mros
|
232
221
|
end
|
233
222
|
|
234
223
|
# Register a Ruby object with Polar.
|
@@ -259,10 +248,82 @@ module Oso
|
|
259
248
|
|
260
249
|
private
|
261
250
|
|
251
|
+
# new/old data filtering core API shared logic
|
252
|
+
def partial_query(actor, action, resource_cls) # rubocop:disable Metrics/MethodLength
|
253
|
+
var_name = 'resource'
|
254
|
+
resource = Variable.new var_name
|
255
|
+
|
256
|
+
partials = query_rule(
|
257
|
+
'allow',
|
258
|
+
actor,
|
259
|
+
action,
|
260
|
+
resource,
|
261
|
+
bindings: { var_name => type_constraint(resource, resource_cls) },
|
262
|
+
accept_expression: true
|
263
|
+
)
|
264
|
+
|
265
|
+
partials.each_with_object([]) do |result, out|
|
266
|
+
result.each do |key, val|
|
267
|
+
out.push prefilter_isas(key, val)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def new_authorized_query(actor, action, resource_class)
|
273
|
+
partials = partial_query(actor, action, resource_class)
|
274
|
+
types = host.serialize_types
|
275
|
+
class_name = class_to_name resource_class
|
276
|
+
plan = ffi.build_data_filter(types, partials, 'resource', class_name)
|
277
|
+
filter = ::Oso::Polar::Data::Filter.parse(self, plan)
|
278
|
+
host.adapter.build_query filter
|
279
|
+
end
|
280
|
+
|
281
|
+
# handle Isa constraints in a partial query
|
282
|
+
def prefilter_isas(key, val) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
283
|
+
# this will usually be the case! sometimes not, if it's an instance.
|
284
|
+
if val.is_a?(Expression) && val.operator == 'And'
|
285
|
+
# get the isas
|
286
|
+
isas, othas = val.args.partition do |expr|
|
287
|
+
expr.operator == 'Isa' &&
|
288
|
+
expr.args[1].is_a?(Pattern) &&
|
289
|
+
expr.args[1].fields.empty?
|
290
|
+
end
|
291
|
+
|
292
|
+
# drop all the isas we can verify now, keep everything else
|
293
|
+
othas += isas.reject do |isa|
|
294
|
+
isa.args[0].is_a? name_to_class isa.args[1].tag
|
295
|
+
end
|
296
|
+
|
297
|
+
# TODO(gw) check the rest of them instead of just adding them?
|
298
|
+
val.args = othas
|
299
|
+
end
|
300
|
+
val = host.to_polar val
|
301
|
+
{ 'bindings' => { key => val } }
|
302
|
+
end
|
303
|
+
|
304
|
+
# get the (maybe user-supplied) name of a class.
|
305
|
+
# kind of a hack because of class autoreloading.
|
306
|
+
def class_to_name(klass) # rubocop:disable Metrics/AbcSize
|
307
|
+
if (rec = host.types[klass]) || (rec = host.types[klass.name])
|
308
|
+
rec.name
|
309
|
+
elsif (rec = host.types.values.find { |v| v.klass.get == klass })
|
310
|
+
host.types[klass] = rec
|
311
|
+
rec.name
|
312
|
+
else
|
313
|
+
raise NameError, "Unknown class `#{klass}`"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def try_class_to_name(klass)
|
318
|
+
class_to_name klass
|
319
|
+
rescue NameError
|
320
|
+
nil
|
321
|
+
end
|
322
|
+
|
262
323
|
def type_constraint(var, cls)
|
263
324
|
Expression.new(
|
264
325
|
'And',
|
265
|
-
[Expression.new('Isa', [var, Pattern.new(
|
326
|
+
[Expression.new('Isa', [var, Pattern.new(class_to_name(cls), {})])]
|
266
327
|
)
|
267
328
|
end
|
268
329
|
|
@@ -276,7 +337,6 @@ module Oso
|
|
276
337
|
# Register MROs, load Polar code, and check inline queries.
|
277
338
|
# @param sources [Array<Source>] Polar sources to load.
|
278
339
|
def load_sources(sources)
|
279
|
-
host.register_mros
|
280
340
|
ffi_polar.load(sources)
|
281
341
|
check_inline_queries
|
282
342
|
end
|