Skip to content

Commit

Permalink
Handle nested array types
Browse files Browse the repository at this point in the history
  • Loading branch information
surma committed Mar 15, 2021
1 parent 165f796 commit 9654953
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 34 deletions.
26 changes: 17 additions & 9 deletions lib/asbind-instance/bind-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ function getFunctionFromKeyPath(baseObject, keys) {
// Converts web platform names for the different ArrayBufferViews
// to the names that ASC understands. Currently, that only means
// to cut off the `Big` in `BigInt64Array`.
const stdlibTypedArrayPrefix = "~lib/typedarray/";
function normalizeArrayBufferViewTypeName(typeName) {
// Don鈥檛 do anything if this is not a stdlib type.
if (!typeName.startsWith(stdlibTypedArrayPrefix)) {
return typeName;
}
typeName = typeName.slice(stdlibTypedArrayPrefix.length);
if (typeName.startsWith("Big")) {
// Slice off `Big` as the loader doesn鈥檛 have that prefix.
typeName = typeName.slice(3);
Expand All @@ -21,7 +27,7 @@ function getTypeId(asbindInstance, typeName) {
if (typeName in asbindInstance.typeDescriptor.typeIds) {
return asbindInstance.typeDescriptor.typeIds[typeName];
}
throw Error(`Unknown type ${JSON.stringifty(typeName)}`);
throw Error(`Unknown type ${JSON.stringify(typeName)}`);
}

function nop(asbindInstance, value, typeName) {
Expand Down Expand Up @@ -49,11 +55,13 @@ function putArrayBuffer(asbindInstance, value, typeName) {
);
}

const stdlibArray = "~lib/array/Array";
function arrayInnerType(typeName) {
if (!typeName.startsWith("Array<")) {
if (!typeName.startsWith(stdlibArray)) {
throw Error(`${JSON.stringify(typeName)} is not an array type`);
}
return typeName.slice("Array<".length, -1);
// Cut off stdlib path + generic angle brackets.
return typeName.slice(`${stdlibArray}<`.length, -1);
}

function getArray(asbindInstance, value, typeName) {
Expand Down Expand Up @@ -81,24 +89,24 @@ const converters = new Map([
[/^void$/, { ascToJs: nop, jsToAsc: nop }],
[/^(i|u)(8|16|32)$/, { ascToJs: nop, jsToAsc: nop }],
[/^f(32|64)$/, { ascToJs: nop, jsToAsc: nop }],
[/^[sS]tring$/, { ascToJs: getString, jsToAsc: putString }],
[/^~lib\/string\/String$/, { ascToJs: getString, jsToAsc: putString }],
[
/^(Ui|I)nt(8|16|32)Array$/,
/^~lib\/typedarray\/(Ui|I)nt(8|16|32)Array$/,
{ ascToJs: getArrayBufferView, jsToAsc: putArrayBuffer }
],
[
/^Big(Ui|I)nt64Array$/,
/^~lib\/typedarray\/Big(Ui|I)nt64Array$/,
{ ascToJs: getArrayBufferView, jsToAsc: putArrayBuffer }
],
[
/^Uint8ClampedArray$/,
/^~lib\/typedarray\/Uint8ClampedArray$/,
{ ascToJs: getArrayBufferView, jsToAsc: putArrayBuffer }
],
[
/^Float(32|64)Array$/,
/^~lib\/typedarray\/Float(32|64)Array$/,
{ ascToJs: getArrayBufferView, jsToAsc: putArrayBuffer }
],
[/^Array<.+>$/, { ascToJs: getArray, jsToAsc: putArray }]
[/^~lib\/array\/Array<.+>$/, { ascToJs: getArray, jsToAsc: putArray }]
]);

const warned = new Set();
Expand Down
10 changes: 10 additions & 0 deletions test/tests/array/asc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ export function swapAndPad(a: Array<f64>, b: f64[]): Array<f64> {
}

declare function swappedConcat(a: f64[], b: Array<f64>): f64[];

export function join(s: Array<Array<string>>): string {
let result: string = "";
for (let outer = 0; outer < s.length; outer++) {
for (let inner = 0; inner < s[outer].length; inner++) {
result += s[outer][inner];
}
}
return result;
}
12 changes: 12 additions & 0 deletions test/tests/array/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,16 @@ describe("as-bind", function() {
[255, 10, 11, 12, 1, 2, 3, 255].join(",")
);
});

it("should handle nested array", async function() {
const instance = await AsBind.instantiate(this.rawModule, {
asc: {
swappedConcat(a, b) {
return [].concat(b, a);
}
}
});
const data = [["a", "b", "c"], ["1"], ["w", "x", "y", "z"]];
assert(instance.exports.join(data) === data.map(s => s.join("")).join(""));
});
});
68 changes: 43 additions & 25 deletions transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ function elementHasFlag(el, flag) {
}

function typeName(type) {
let name = type.name.text ?? type.name.identifier.text;
if (type.typeArguments.length > 0) {
name = `${name}<${type.typeArguments.map(typeName).join(",")}>`;
}
return name;
return type.getClass()?.internalName ?? type.toString();
}

function containingModule(func) {
Expand All @@ -29,26 +25,34 @@ function containingModule(func) {

function getFunctionTypeDescriptor(func) {
return {
returnType: typeName(func.declaration.signature.returnType),
parameters: func.declaration.signature.parameters.map(parameter =>
typeName(parameter.type)
returnType: typeName(func.signature.returnType),
parameters: func.signature.parameterTypes.map(parameter =>
typeName(parameter)
)
};
}

function extractTypeMap(func) {
// TODO: Generics?
func = func.instances.get("");
const result = {
[typeName(
func.declaration.signature.returnType
)]: func.signature.returnType?.getClass?.()?.id
};
func.declaration.signature.parameters.forEach((parameter, i) => {
result[typeName(parameter.type)] = func.signature.parameterTypes[
i
].getClass?.()?.id;
});
function extractTypeIds(type) {
const result = {};
const clazz = type.getClass?.();
if (!clazz) {
return result;
}
result[clazz.internalName] = clazz.id;
if (clazz.typeArguments) {
for (const subType of clazz.typeArguments) {
Object.assign(result, extractTypeIds(subType));
}
}
return result;
}

function extractTypeIdsFromFunction(func) {
const result = {};
Object.assign(result, extractTypeIds(func.signature.returnType));
func.signature.parameterTypes.forEach(paramType =>
Object.assign(result, extractTypeIds(paramType))
);
return result;
}

Expand Down Expand Up @@ -78,7 +82,14 @@ class AsBindTransform extends Transform {

const typeIds = {};
const importedFunctions = {};
for (const importedFunction of flatImportedFunctions) {
for (let importedFunction of flatImportedFunctions) {
if (
importedFunction.instances.size > 1 ||
!importedFunction.instances.has("")
) {
throw Error(`Can鈥檛 import or export generic functions.`);
}
importedFunction = importedFunction.instances.get("");
// To know under what module name an imported function will be expected,
// we have to find the containing module of the given function, take the
// internal name (which is effectively the file path without extension)
Expand All @@ -93,14 +104,21 @@ class AsBindTransform extends Transform {
importedFunctions[moduleName][
importedFunction.name
] = getFunctionTypeDescriptor(importedFunction);
Object.assign(typeIds, extractTypeMap(importedFunction));
Object.assign(typeIds, extractTypeIdsFromFunction(importedFunction));
}
const exportedFunctions = {};
for (const exportedFunction of flatExportedFunctions) {
for (let exportedFunction of flatExportedFunctions) {
if (
exportedFunction.instances.size > 1 ||
!exportedFunction.instances.has("")
) {
throw Error(`Can鈥檛 import or export generic functions.`);
}
exportedFunction = exportedFunction.instances.get("");
exportedFunctions[exportedFunction.name] = getFunctionTypeDescriptor(
exportedFunction
);
Object.assign(typeIds, extractTypeMap(exportedFunction));
Object.assign(typeIds, extractTypeIdsFromFunction(exportedFunction));
}
this.typeData = JSON.stringify({
typeIds,
Expand Down

0 comments on commit 9654953

Please sign in to comment.