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.
@@ -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], :int32
18
- attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], :int32
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], FFI::Query
22
- attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32], FFI::Query
23
- attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string], :int32
24
- attach_function :register_mro, :polar_register_mro, [FFI::Polar, :string, :string], :int32
25
- attach_function :next_message, :polar_next_polar_message, [FFI::Polar], FFI::Message
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
- :string
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
- polar = Rust.new
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
- handle_error if plan.nil?
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
- handle_error if loaded.zero?
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
- handle_error if cleared.zero?
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
- id = Rust.new_id(self)
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
- handle_error if query.null?
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
- handle_error if query.null?
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
- handle_error if registered.zero?
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
- handle_error if registered.zero?
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.process(enrich_message)
155
+ process_message(message, enrich_message)
138
156
  end
139
157
  end
140
158
 
141
- def handle_error
142
- raise FFI::Error.get(enrich_message)
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
@@ -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], :int32
17
- attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string], :int32
18
- attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32], :int32
19
- attach_function :application_error, :polar_application_error, [FFI::Query, :string], :int32
20
- attach_function :next_event, :polar_next_query_event, [FFI::Query], FFI::QueryEvent
21
- attach_function :next_message, :polar_next_query_message, [FFI::Query], FFI::Message
22
- attach_function :source, :polar_query_source_info, [FFI::Query], FFI::Source
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 :bind, :polar_bind, [FFI::Query, :string, :string], :int32
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
- handle_error if res.zero?
34
+ check_result res
34
35
  end
35
36
 
36
- # @param result [String]
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(result, call_id:)
40
- res = Rust.call_result(self, call_id, result)
41
- handle_error if res.zero?
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
- handle_error if res.zero?
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
- handle_error if res.zero?
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
- handle_error if event.null?
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
- handle_error if res.zero?
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.process(enrich_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
- handle_error if res.null?
107
+ res = check_result res
93
108
 
94
109
  res.to_s
95
110
  end
96
111
 
97
- def handle_error
98
- raise FFI::Error.get(enrich_message)
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 Source FFI pointer.
7
- class Source < ::FFI::AutoPointer
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, [Message], :int32
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 self.release(ptr)
28
- Rust.free(ptr) unless ptr.null?
32
+ def zero?
33
+ null?
29
34
  end
30
- end
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
- # Wrapper class for Error FFI pointer + operations.
38
- class Error < ::FFI::AutoPointer
39
- def self.release(ptr)
40
- Rust.free(ptr) unless ptr.null?
40
+
41
+ # Wrapper class for Rust strings FFI pointer + operations.
42
+ class RustString < ::FFI::AutoPointer
43
+ def zero?
44
+ null?
41
45
  end
42
- end
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
- # Wrapper class for Source FFI pointer.
51
- class Source < ::FFI::AutoPointer
52
- def self.release(ptr)
53
- Rust.free(ptr) unless ptr.null?
54
- end
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/message'
66
- require 'oso/polar/ffi/source'
74
+ require 'oso/polar/ffi/rust_string'
@@ -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
- raise UnregisteredClassError, name unless types.key? name
100
+ typ = types[name]
101
+ raise UnregisteredClassError, name if typ.nil?
99
102
 
100
- types[name].klass.get
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
- instance_id = types[value].id if value.is_a?(Class) && types.key?(value)
320
- { 'ExternalInstance' => { 'instance_id' => cache_instance(value, id: instance_id), 'repr' => nil } }
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
- if value.key? 'Float'
361
+ case value.keys.first
362
+ when 'Float'
341
363
  case num
342
364
  when 'Infinity'
343
- return Float::INFINITY
365
+ Float::INFINITY
344
366
  when '-Infinity'
345
- return -Float::INFINITY
367
+ -Float::INFINITY
346
368
  when 'NaN'
347
- return Float::NAN
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'
@@ -84,20 +84,8 @@ module Oso
84
84
  @ffi_polar
85
85
  end
86
86
 
87
- # get the (maybe user-supplied) name of a class.
88
- # kind of a hack because of class autoreloading.
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(get_class_name(cls), {})])]
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