Skip to content

Commit 2631806

Browse files
patrickodgj
andauthored
{Python,Ruby,JS}: Add class_id information to ExternalInstance values; use for in-VM IsA checks (#1472)
Create `Constants` type which wraps two HashMaps describing Symbol -> Term and ID -> Symbol relationships. This second ID -> Symbol index is populated by reading the optional `class_id` information in `ExternalInstance` and will be used to implement in-core IsA checks where present. Extend `vm#isa` to attempt an in-core check when the `ExternalInstance` value we're processing has the necessary `class_id` information declared. At time of commit Python is the only language which is capabale of passing this information to the core. * {JS,Python,Ruby} Format ExternalInstance values with user-provided class names if present (#1488) Co-authored-by: Gabe Jackson <17556281+gj@users.noreply.github.com>
1 parent 59e572a commit 2631806

File tree

21 files changed

+280
-25
lines changed

21 files changed

+280
-25
lines changed

languages/js/src/Host.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,29 @@ export class Host<Query, Resource> {
456456
}
457457
default: {
458458
let instanceId: number | undefined = undefined;
459-
if (isConstructor(v)) instanceId = this.getType(v)?.id;
459+
let classId: number | undefined = undefined;
460460

461461
// pass a string class repr *for registered types only*, otherwise pass
462462
// undefined (allow core to differentiate registered or not)
463463
const v_cast = v as NullishOrHasConstructor;
464-
let classRepr: string | undefined = v_cast?.constructor?.name;
464+
let classRepr: string | undefined = undefined;
465+
466+
if (isConstructor(v)) {
467+
instanceId = this.getType(v)?.id;
468+
classId = instanceId;
469+
classRepr = this.getType(v)?.name;
470+
} else {
471+
const v_constructor: Class | undefined = v_cast?.constructor;
472+
473+
// pass classId for instances of *registered classes* only
474+
if (v_constructor !== undefined && this.types.has(v_constructor)) {
475+
classId = this.getType(v_constructor)?.id;
476+
classRepr = this.getType(v_constructor)?.name;
477+
}
478+
}
479+
480+
// pass classRepr for *registered* classes only, pass undefined
481+
// otherwise
465482
if (classRepr !== undefined && !this.types.has(classRepr)) {
466483
classRepr = undefined;
467484
}
@@ -474,6 +491,7 @@ export class Host<Query, Resource> {
474491
constructor: undefined,
475492
repr: repr(v),
476493
class_repr: classRepr,
494+
class_id: classId,
477495
},
478496
},
479497
};

languages/js/src/Polar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ export class Polar<Query, Resource> {
171171

172172
// Register MROs, load Polar code, and check inline queries.
173173
private async loadSources(sources: Source[]): Promise<void> {
174-
this.getHost().registerMros();
175174
this.#ffiPolar.load(sources);
176175
this.processMessages();
177176
return this.checkInlineQueries();
@@ -244,6 +243,7 @@ export class Polar<Query, Resource> {
244243
registerClass(cls: Class, params?: ClassParams): void {
245244
const clsName = this.getHost().cacheClass(cls, params);
246245
this.registerConstant(cls, clsName);
246+
this.getHost().registerMros();
247247
}
248248

249249
/**

languages/js/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ interface PolarInstance {
180180
instance_id: number;
181181
repr: string;
182182
class_repr?: string;
183+
class_id?: number;
183184
constructor?: PolarTerm;
184185
};
185186
}
@@ -691,6 +692,7 @@ export interface ClassParams {
691692
* `name` property.
692693
*/
693694
name?: string;
695+
694696
/**
695697
* A Map or object with string keys containing types for fields. Used for
696698
* data filtering.

languages/python/oso/polar/host.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,23 +294,30 @@ def to_polar(self, v):
294294
# ]
295295
# }
296296
else:
297-
instance_id = None
298297
import inspect
299298

299+
instance_id = None
300+
class_id = None
301+
300302
# maintain consistent IDs for registered classes
301303
if inspect.isclass(v):
302304
if v in self.types:
303-
instance_id = self.types[v].id
305+
class_id = instance_id = self.types[v].id
304306

305307
# pass the class_repr only for registered types otherwise None
306-
class_repr = type(v).__name__
307-
class_repr = class_repr if class_repr in self.types else None
308+
class_repr = self.types[type(v)].name if type(v) in self.types else None
309+
310+
# pass class_id for classes & instances of registered classes,
311+
# otherwise pass None
312+
if type(v) in self.types:
313+
class_id = self.types[type(v)].id
308314

309315
val = {
310316
"ExternalInstance": {
311317
"instance_id": self.cache_instance(v, instance_id),
312318
"repr": None,
313319
"class_repr": class_repr,
320+
"class_id": class_id,
314321
}
315322
}
316323
term = {"value": val}

languages/python/oso/polar/polar.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ def load_str(self, string: str):
9595

9696
# Register MROs, load Polar code, and check inline queries.
9797
def _load_sources(self, sources: List[Source]):
98-
self.host.register_mros()
9998
self.ffi_polar.load(sources)
10099
self.check_inline_queries()
101100

@@ -241,12 +240,14 @@ def register_class(
241240
data filtering.
242241
"""
243242
# TODO: let's add example usage here or at least a proper docstring for the arguments
244-
cls_name = self.host.cache_class(
243+
244+
name = self.host.cache_class(
245245
cls,
246246
name=name,
247247
fields=fields,
248248
)
249-
self.register_constant(cls, cls_name)
249+
self.register_constant(cls, name)
250+
self.host.register_mros()
250251

251252
def register_constant(self, value, name):
252253
"""

languages/ruby/lib/oso/polar/host.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,17 +319,24 @@ def to_polar(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplex
319319
end
320320
else
321321
instance_id = nil
322-
instance_id = types[value].id if value.is_a?(Class) && types.key?(value)
323-
324-
# only pass class_repr for registered types
322+
class_id = nil
325323
class_repr = nil
326-
class_repr = value.class.to_s if types.key?(value.class)
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
327333

328334
{
329335
'ExternalInstance' => {
330336
'instance_id' => cache_instance(value, id: instance_id),
331337
'repr' => nil,
332-
'class_repr' => class_repr
338+
'class_repr' => class_repr,
339+
'class_id' => class_id
333340
}
334341
}
335342
end

languages/ruby/lib/oso/polar/polar.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ def register_class(cls, name: nil, fields: nil, combine_query: nil, build_query:
217217
exec_query: exec_query || maybe_mtd(cls, :exec_query)
218218
)
219219
register_constant(cls, name: name)
220+
host.register_mros
220221
end
221222

222223
# Register a Ruby object with Polar.
@@ -336,7 +337,6 @@ def maybe_mtd(cls, mtd)
336337
# Register MROs, load Polar code, and check inline queries.
337338
# @param sources [Array<Source>] Polar sources to load.
338339
def load_sources(sources)
339-
host.register_mros
340340
ffi_polar.load(sources)
341341
check_inline_queries
342342
end

languages/rust/oso/src/host/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ This may mean you performed an operation in your policy over an unbound variable
113113
instance_id: id,
114114
repr: Some(std::any::type_name::<Self>().to_owned()),
115115
class_repr: Some(std::any::type_name::<Self>().to_owned()),
116+
class_id: None,
116117
})
117118
}
118119
PolarValue::List(l) => {

polar-core/src/constants.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::terms::{Symbol, Term};
2+
use std::collections::HashMap;
3+
4+
#[derive(Default, Debug)]
5+
pub(crate) struct Constants {
6+
// Symbol -> Term (populated by *all* constants)
7+
pub symbol_to_term: HashMap<Symbol, Term>,
8+
// Symbol -> class_id (populated by class constants)
9+
class_symbol_to_id: HashMap<Symbol, u64>,
10+
// class_id -> Symbol (populated by class constants)
11+
class_id_to_symbol: HashMap<u64, Symbol>,
12+
}
13+
14+
impl Constants {
15+
pub(crate) fn insert(&mut self, name: Symbol, value: Term) {
16+
self.symbol_to_term.insert(name, value);
17+
}
18+
19+
pub(crate) fn insert_class(&mut self, name: Symbol, value: Term, class_id: u64) {
20+
self.insert(name.clone(), value);
21+
self.class_symbol_to_id.insert(name.clone(), class_id);
22+
self.class_id_to_symbol.insert(class_id, name);
23+
}
24+
25+
pub(crate) fn contains_key(&self, name: &Symbol) -> bool {
26+
self.symbol_to_term.contains_key(name)
27+
}
28+
29+
pub(crate) fn get(&self, name: &Symbol) -> Option<&Term> {
30+
self.symbol_to_term.get(name)
31+
}
32+
33+
pub(crate) fn get_class_id_for_symbol(&self, symbol: &Symbol) -> Option<&u64> {
34+
self.class_symbol_to_id.get(symbol)
35+
}
36+
37+
pub(crate) fn get_symbol_for_class_id(&self, id: &u64) -> Option<&Symbol> {
38+
self.class_id_to_symbol.get(id)
39+
}
40+
}

polar-core/src/folder.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ pub fn fold_external_instance<T: Folder>(
147147
constructor,
148148
repr,
149149
class_repr,
150+
class_id,
150151
}: ExternalInstance,
151152
fld: &mut T,
152153
) -> ExternalInstance {
@@ -155,6 +156,7 @@ pub fn fold_external_instance<T: Folder>(
155156
constructor: constructor.map(|t| fld.fold_term(t)),
156157
repr: repr.map(|r| fld.fold_string(r)),
157158
class_repr: class_repr.map(|r| fld.fold_string(r)),
159+
class_id: class_id.map(|id| fld.fold_instance_id(id)),
158160
}
159161
}
160162

@@ -271,6 +273,7 @@ mod tests {
271273
constructor: None,
272274
repr: None,
273275
class_repr: None,
276+
class_id: None,
274277
}));
275278
let instance_pattern = term!(value!(Pattern::Instance(InstanceLiteral {
276279
tag: sym!("d"),

0 commit comments

Comments
 (0)