oso-oso 0.23.0 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|