Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions tslang/lib/TypeScript/MLIRGenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,59 @@ namespace mlirgen
return mlir::failure();
}

// If the destination has a known field order (e.g. a type-literal
// annotation on the receiving var/let), re-lay-out this literal's own
// storage to match it BEFORE method bodies are generated. Without
// this, the literal's storage stays in "all fields, then all
// methods" order (mlirGenObjectLiteralFields/-MethodPrototypes run as
// two separate passes over the properties, by design - methods may
// need every field already registered), which only coincidentally
// matches a declared type whose members interleave fields and
// methods in a different order. Method bodies compiled against the
// MISMATCHED order silently read/write the wrong byte offset when
// later invoked against the (differently laid out) destination
// memory - no crash, no error, just corrupted data: a `this.field`
// write can land on a neighboring method's function-pointer slot.
// Field position resolution inside method bodies is by NAME against
// objectStorageType's CURRENT field list (TupleFieldType/getIndex,
// MLIRCodeLogic.h), so reordering here - before
// mlirGenObjectLiteralMethodBodies runs - is sufficient; no
// per-method index patching needed. Must run after
// mlirGenObjectLiteralCaptures (methodInfosWithCaptures already holds
// pre-reorder indices consumed there) and before the final
// setFields/method-bodies pass below.
llvm::SmallVector<mlir_ts::FieldInfo> receiverFields;
if (oli.receiverType && mlir::succeeded(mth.getFields(oli.receiverType, receiverFields, true)) &&
receiverFields.size() == oli.fieldInfos.size())
{
llvm::SmallVector<mlir::Attribute> reorderedValues;
llvm::SmallVector<mlir_ts::FieldInfo> reorderedFieldInfos;
reorderedValues.reserve(oli.fieldInfos.size());
reorderedFieldInfos.reserve(oli.fieldInfos.size());

auto allMatched = true;
for (auto &receiverField : receiverFields)
{
auto foundIt = llvm::find_if(oli.fieldInfos,
[&](const mlir_ts::FieldInfo &fieldInfo) { return fieldInfo.id == receiverField.id; });
if (foundIt == oli.fieldInfos.end())
{
allMatched = false;
break;
}

auto index = std::distance(oli.fieldInfos.begin(), foundIt);
reorderedFieldInfos.push_back(*foundIt);
reorderedValues.push_back(oli.values[index]);
}

if (allMatched)
{
oli.fieldInfos = std::move(reorderedFieldInfos);
oli.values = std::move(reorderedValues);
}
}

// final type, update
objectStorageType.setFields(oli.fieldInfos);

Expand Down
4 changes: 4 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ add_test(NAME test-compile-00-interface-function-typed-field COMMAND test-runner
add_test(NAME test-compile-00-interface-captures COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-compile-00-object-annotated-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-compile-00-object-annotated-method-params COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-compile-00-object-annotated-method-interleaved COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-compile-00-interface-object-array COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-compile-00-interface-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-compile-00-interface-new COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
Expand Down Expand Up @@ -645,6 +646,7 @@ add_test(NAME test-jit-00-interface-function-typed-field COMMAND test-runner -ji
add_test(NAME test-jit-00-interface-captures COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-jit-00-object-annotated-method COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-jit-00-object-annotated-method-params COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-jit-00-object-annotated-method-interleaved COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-jit-00-interface-object-array COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-jit-00-interface-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-jit-00-interface-new COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
Expand Down Expand Up @@ -857,6 +859,7 @@ add_test(NAME test-compile-shared-export-import-object-literal-untyped COMMAND t
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-multi-method COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_multi_method.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-interleaved COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_interleaved.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_interleaved.ts")
add_test(NAME test-compile-shared-export-import-vars COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-compile-shared-export-import-vars-2 COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-compile-shared-export-import-enum COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
Expand All @@ -876,6 +879,7 @@ add_test(NAME test-jit-shared-export-import-object-literal-untyped COMMAND test-
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-multi-method COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_multi_method.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-interleaved COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_interleaved.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_interleaved.ts")
add_test(NAME test-jit-shared-export-import-vars COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-jit-shared-export-import-vars-2 COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-jit-shared-export-import-enum COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
40 changes: 40 additions & 0 deletions tslang/test/tester/tests/00object_annotated_method_interleaved.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Extends 00object_annotated_method_params.ts's coverage: that test's
// type-literal annotations always declare all fields before all methods
// (total; add(); addTwice()). Here fields and methods INTERLEAVE in
// declaration order (base; addBase(); total; add(); ...) - this used to
// silently corrupt data: the object literal's own storage laid out as
// "fields, then methods" regardless of the annotation's order, so a method
// compiled against that layout read/wrote the wrong byte offset once
// invoked against the annotation-ordered variable, up to overwriting a
// sibling method's function pointer with a field's value. See
// docs/object-literal-boxing-design.md-adjacent history; root-caused and
// fixed via a field/value reorder pass in mlirGen(ObjectLiteralExpression).

function main() {
let acc: { base: number; addBase(n: number): void; total: number; add(n: number): void; addTwice(n: number): void; scaled(factor: number): number } = {
base: 100,
addBase(n: number) { this.base = this.base + n; },
total: 0,
add(n: number) { this.total = this.total + n; },
addTwice(n: number) { this.add(n); this.add(n); },
scaled(factor: number) { return this.total * factor; },
};

acc.addBase(5);
assert(acc.base == 105);
print(acc.base);

acc.add(3);
assert(acc.total == 3);
print(acc.total);

acc.addTwice(2);
assert(acc.total == 7);
print(acc.total);

const result = acc.scaled(2);
assert(result == 14);
print(result);

print("done.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace A {

export interface Accumulator {
base: number;
addBase(n: number): void;
total: number;
add(n: number): void;
addTwice(n: number): void;
scaled(factor: number): number;
}

// extends export_object_literal_structural_typed_multi_method.ts's
// coverage: that test's methods are all grouped after every field
// (total; add(); addTwice(); scaled()). Here fields and methods
// INTERLEAVE in declaration order (base; addBase(); total; add(); ...) -
// see 00object_annotated_method_interleaved.ts for the same-module
// version of this coverage and the mechanism this used to break.
export var acc: {
base: number;
addBase(n: number): void;
total: number;
add(n: number): void;
addTwice(n: number): void;
scaled(factor: number): number;
} = {
base: 100.0,
addBase(n: number) { this.base = this.base + n; },
total: 0.0,
add(n: number) { this.total = this.total + n; },
addTwice(n: number) { this.add(n); this.add(n); },
scaled(factor: number) { return this.total * factor; },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import './export_object_literal_structural_typed_interleaved'

var acc: A.Accumulator = <A.Accumulator>A.acc;

acc.addBase(5);
print(acc.base);
assert(acc.base == 105);

acc.add(3);
print(acc.total);
assert(acc.total == 3);

acc.addTwice(2);
print(acc.total);
assert(acc.total == 7);

print(acc.scaled(2));
assert(acc.scaled(2) == 14);

print("done.");
Loading