Skip to content

Commit 472c5bc

Browse files
committed
fix: struct ordering
Before objects were either inserted before already inserted objects that reference them or at the end of the list. This algorithm fails when structs are reference structs that reference other structs. Now dependent objects are always inserted first. To implement quickly it naively iterates over the object list N times to insert all of the remaining objects.
1 parent 34d5259 commit 472c5bc

2 files changed

Lines changed: 4509 additions & 22 deletions

File tree

src/index.ts

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,6 @@ function cppIdentifer(s: string) {
66
return helpers.snakeToPascalCase(s);
77
}
88

9-
function objectReferencesObject(existing: ObjectType, name: string) {
10-
for (const prop of existing.properties) {
11-
if (prop.kind === 'object') {
12-
const object = prop as ObjectType
13-
if (object.name === name) {
14-
return true
15-
}
16-
}
17-
}
18-
return false
19-
}
20-
219
function v2ToCppTypeXInner(type: XtpNormalizedType, refnamespace: string): string {
2210
switch (type.kind) {
2311
case 'string':
@@ -328,29 +316,89 @@ function isTypeUntypedObject(type: XtpNormalizedType) {
328316
return false
329317
}
330318

319+
function addObject(objects: Schema[], schema: Schema) {
320+
// find the object's direct dependencies
321+
const names = new Set();
322+
const object = schema.xtpType as ObjectType
323+
for (const prop of object.properties) {
324+
if (prop.kind === 'object') {
325+
const object = prop as ObjectType
326+
if (object.name) {
327+
names.add(object.name)
328+
}
329+
} else if (prop.kind === 'array') {
330+
const arrayType = prop as ArrayType
331+
if (arrayType.elementType.kind === 'object') {
332+
const object = arrayType.elementType as ObjectType
333+
if (object.name) {
334+
names.add(object.name)
335+
}
336+
}
337+
} else if (prop.kind === 'map') {
338+
const { keyType, valueType } = prop as MapType
339+
if (valueType.kind === 'object') {
340+
const object = valueType as ObjectType
341+
if (object.name) {
342+
names.add(object.name)
343+
}
344+
}
345+
}
346+
}
347+
// loop until after the dependencies or end of objects
348+
let i = 0;
349+
for (; i < objects.length; i++) {
350+
if (names.size === 0) {
351+
break
352+
}
353+
const existing = objects[i]
354+
const existingObject = existing.xtpType as ObjectType;
355+
names.delete(existingObject.name)
356+
}
357+
// not all deps found, can't add yet
358+
if (names.size) {
359+
return false
360+
}
361+
// add
362+
objects.splice(i, 0, schema);
363+
return true
364+
}
365+
331366
export function render() {
332367
const tmpl = Host.inputString();
333368
const prevctx = getContext();
334369

335370
const enums: Schema[] = [];
371+
const todoObjects: Schema[] = [];
336372
const objects: Schema[] = [];
337-
Object.values(prevctx.schema.schemas).forEach(schema => {
373+
for (const schema of Object.values(prevctx.schema.schemas)) {
338374
if (helpers.isEnum(schema)) {
339375
enums.push(schema);
340376
} else if (helpers.isObject(schema)) {
341-
const object = schema.xtpType as ObjectType;
342-
// insertion sort objects ahead of objects that use them
343-
let i = 0;
344-
for (; i < objects.length; i++) {
345-
if (objectReferencesObject(objects[i].xtpType as ObjectType, object.name)) {
346-
break;
347-
}
377+
// insertion sort add object after its dependencies
378+
// if it's dependencies aren't in objects, defer
379+
if (!addObject(objects, schema)) {
380+
todoObjects.push(schema)
348381
}
349-
objects.splice(i, 0, schema); // we need Schema as it has the required attribute
350382
} else {
351383
throw new Error("unhandled schema type " + schema.xtpType.kind)
352384
}
353-
});
385+
}
386+
// add the remaining objects
387+
// rather than doing dependency analysis
388+
// each iteration loop through all the objects until we add an object
389+
const maxIterations = todoObjects.length
390+
for (let iter = 0; iter < maxIterations; iter++) {
391+
for (let i = 0; i < todoObjects.length; i++) {
392+
if (addObject(objects, todoObjects[i])) {
393+
todoObjects.splice(i, 1)
394+
break
395+
}
396+
}
397+
}
398+
if (todoObjects.length) {
399+
throw new Error("Failed to add remaning objects")
400+
}
401+
354402
// sort the properties for efficient struct layout
355403
for (const object of objects) {
356404
object.properties.sort((a: Property, b: Property) => {

0 commit comments

Comments
 (0)