Skip to content

Commit 192b36b

Browse files
authored
feat: Implement Directions (#1852)
1 parent b36d613 commit 192b36b

30 files changed

+1330
-122
lines changed

src.compiler/BuilderHelpers.ts

+53
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ export function isMap(type: ts.Type | null): boolean {
211211
return !!(type && type.symbol?.name === 'Map');
212212
}
213213

214+
export function isSet(type: ts.Type | null): boolean {
215+
return !!(type && type.symbol?.name === 'Set');
216+
}
217+
214218
function markNodeSynthesized(node: ts.Node): ts.Node {
215219
for (const c of node.getChildren()) {
216220
markNodeSynthesized(c);
@@ -259,3 +263,52 @@ export function cloneTypeNode<T extends ts.Node>(node: T): T {
259263

260264
throw new Error(`Unsupported TypeNode: '${ts.SyntaxKind[node.kind]}' extend type node cloning`);
261265
}
266+
267+
268+
export function isPrimitiveToJson(type: ts.Type, typeChecker: ts.TypeChecker) {
269+
if (!type) {
270+
return false;
271+
}
272+
273+
const isArray = isTypedArray(type);
274+
const arrayItemType = unwrapArrayItemType(type, typeChecker);
275+
276+
if (hasFlag(type, ts.TypeFlags.Unknown)) {
277+
return true;
278+
}
279+
if (hasFlag(type, ts.TypeFlags.Number)) {
280+
return true;
281+
}
282+
if (hasFlag(type, ts.TypeFlags.String)) {
283+
return true;
284+
}
285+
if (hasFlag(type, ts.TypeFlags.Boolean)) {
286+
return true;
287+
}
288+
289+
if (arrayItemType) {
290+
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) {
291+
return true;
292+
}
293+
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) {
294+
return true;
295+
}
296+
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) {
297+
return true;
298+
}
299+
} else if (type.symbol) {
300+
switch (type.symbol.name) {
301+
case 'Uint8Array':
302+
case 'Uint16Array':
303+
case 'Uint32Array':
304+
case 'Int8Array':
305+
case 'Int16Array':
306+
case 'Int32Array':
307+
case 'Float32Array':
308+
case 'Float64Array':
309+
return true;
310+
}
311+
}
312+
313+
return false;
314+
}

src.compiler/typescript/Serializer.setProperty.ts

+78-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as ts from 'typescript';
2-
import { cloneTypeNode, createNodeFromSource, setMethodBody } from '../BuilderHelpers';
2+
import { cloneTypeNode, createNodeFromSource, isPrimitiveToJson, isSet, setMethodBody } from '../BuilderHelpers';
33
import { isPrimitiveType } from '../BuilderHelpers';
44
import { hasFlag } from '../BuilderHelpers';
55
import { getTypeWithNullableInfo } from '../BuilderHelpers';
@@ -301,6 +301,83 @@ function generateSetPropertyBody(
301301
)
302302
);
303303

304+
caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue()));
305+
} else if (isSet(type.type)) {
306+
const setType = type.type as ts.TypeReference;
307+
if (!isPrimitiveType(setType.typeArguments![0])) {
308+
throw new Error('only Set<Primitive> maps are supported extend if needed!');
309+
}
310+
311+
const collectionAddMethod = ts
312+
.getJSDocTags(prop.property)
313+
.filter(t => t.tagName.text === 'json_add')
314+
.map(t => t.comment ?? '')[0] as string;
315+
316+
if (isPrimitiveFromJson(setType.typeArguments![0], typeChecker)) {
317+
importer(
318+
setType.typeArguments![0].symbol!.name,
319+
findModule(setType.typeArguments![0], program.getCompilerOptions())
320+
);
321+
322+
if(collectionAddMethod) {
323+
caseStatements.push(
324+
createNodeFromSource<ts.ForOfStatement>(
325+
`for (const i of (v as unknown[])) {
326+
obj.${collectionAddMethod}(i as ${
327+
setType.typeArguments![0].symbol!.name
328+
});
329+
}`,
330+
ts.SyntaxKind.ForOfStatement
331+
)
332+
);
333+
} else {
334+
caseStatements.push(
335+
assignField(
336+
createNodeFromSource<ts.NewExpression>(
337+
`new Set<${setType.typeArguments![0].symbol!.name}>(v as ${
338+
setType.typeArguments![0].symbol!.name
339+
}[])!`,
340+
ts.SyntaxKind.NewExpression
341+
)
342+
)
343+
);
344+
}
345+
346+
} else if (isEnumType(setType.typeArguments![0])) {
347+
importer(
348+
setType.typeArguments![0].symbol!.name,
349+
findModule(setType.typeArguments![0], program.getCompilerOptions())
350+
);
351+
importer('JsonHelper', '@src/io/JsonHelper');
352+
353+
if(collectionAddMethod) {
354+
caseStatements.push(
355+
createNodeFromSource<ts.ForOfStatement>(
356+
`for (const i of (v as number[]) ) {
357+
obj.${collectionAddMethod}(JsonHelper.parseEnum<${
358+
setType.typeArguments![0].symbol!.name
359+
}>(i, ${setType.typeArguments![0].symbol!.name})!);
360+
}`,
361+
ts.SyntaxKind.ForOfStatement
362+
)
363+
);
364+
} else {
365+
// obj.field = new Set<EnumType>((v! as number[]).map(i => JsonHelper.parseEnum<EnumType>(v!, EnumType)));
366+
caseStatements.push(
367+
assignField(
368+
createNodeFromSource<ts.NewExpression>(
369+
`new Set<${
370+
setType.typeArguments![0].symbol!.name
371+
}>( (v! as number[]).map(i => JsonHelper.parseEnum<${
372+
setType.typeArguments![0].symbol!.name
373+
}>(v, ${setType.typeArguments![0].symbol!.name})!`,
374+
ts.SyntaxKind.NewExpression
375+
)
376+
)
377+
);
378+
}
379+
}
380+
304381
caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue()));
305382
} else if (isImmutable(type.type)) {
306383
let itemSerializer = type.type.symbol.name;

src.compiler/typescript/Serializer.toJson.ts

+72-68
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,12 @@ import {
88
isTypedArray,
99
unwrapArrayItemType,
1010
isMap,
11-
isEnumType
11+
isEnumType,
12+
isSet,
13+
isPrimitiveToJson
1214
} from '../BuilderHelpers';
1315
import { findModule, findSerializerModule, isImmutable, JsonProperty, JsonSerializable } from './Serializer.common';
1416

15-
function isPrimitiveToJson(type: ts.Type, typeChecker: ts.TypeChecker) {
16-
if (!type) {
17-
return false;
18-
}
19-
20-
const isArray = isTypedArray(type);
21-
const arrayItemType = unwrapArrayItemType(type, typeChecker);
22-
23-
if (hasFlag(type, ts.TypeFlags.Unknown)) {
24-
return true;
25-
}
26-
if (hasFlag(type, ts.TypeFlags.Number)) {
27-
return true;
28-
}
29-
if (hasFlag(type, ts.TypeFlags.String)) {
30-
return true;
31-
}
32-
if (hasFlag(type, ts.TypeFlags.Boolean)) {
33-
return true;
34-
}
35-
36-
if (arrayItemType) {
37-
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) {
38-
return true;
39-
}
40-
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) {
41-
return true;
42-
}
43-
if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) {
44-
return true;
45-
}
46-
} else if (type.symbol) {
47-
switch (type.symbol.name) {
48-
case 'Uint8Array':
49-
case 'Uint16Array':
50-
case 'Uint32Array':
51-
case 'Int8Array':
52-
case 'Int16Array':
53-
case 'Int32Array':
54-
case 'Float32Array':
55-
case 'Float64Array':
56-
return true;
57-
}
58-
}
59-
60-
return false;
61-
}
6217

6318
function generateToJsonBody(
6419
program: ts.Program,
@@ -96,7 +51,6 @@ function generateToJsonBody(
9651
}
9752

9853
let propertyStatements: ts.Statement[] = [];
99-
10054

10155
const typeChecker = program.getTypeChecker();
10256
const type = getTypeWithNullableInfo(typeChecker, prop.property.type!, prop.asRaw);
@@ -154,7 +108,6 @@ function generateToJsonBody(
154108
)
155109
);
156110
}
157-
158111
} else if (isMap(type.type)) {
159112
const mapType = type.type as ts.TypeReference;
160113
if (!isPrimitiveType(mapType.typeArguments![0])) {
@@ -170,7 +123,9 @@ function generateToJsonBody(
170123
for(const [k, v] of obj.${fieldName}!) {
171124
m.set(k.toString(), v);
172125
}
173-
}`, ts.SyntaxKind.Block);
126+
}`,
127+
ts.SyntaxKind.Block
128+
);
174129
} else if (isEnumType(mapType.typeArguments![1])) {
175130
serializeBlock = createNodeFromSource<ts.Block>(
176131
`{
@@ -179,7 +134,9 @@ function generateToJsonBody(
179134
for(const [k, v] of obj.${fieldName}!) {
180135
m.set(k.toString(), v as number);
181136
}
182-
}`, ts.SyntaxKind.Block);
137+
}`,
138+
ts.SyntaxKind.Block
139+
);
183140
} else {
184141
const itemSerializer = mapType.typeArguments![1].symbol.name + 'Serializer';
185142
importer(itemSerializer, findSerializerModule(mapType.typeArguments![1], program.getCompilerOptions()));
@@ -191,20 +148,68 @@ function generateToJsonBody(
191148
for(const [k, v] of obj.${fieldName}!) {
192149
m.set(k.toString(), ${itemSerializer}.toJson(v));
193150
}
194-
}`, ts.SyntaxKind.Block);
151+
}`,
152+
ts.SyntaxKind.Block
153+
);
154+
}
155+
156+
if (type.isNullable) {
157+
propertyStatements.push(
158+
ts.factory.createIfStatement(
159+
ts.factory.createBinaryExpression(
160+
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName),
161+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
162+
ts.factory.createNull()
163+
),
164+
serializeBlock
165+
)
166+
);
167+
} else {
168+
propertyStatements.push(serializeBlock);
169+
}
170+
} else if (isSet(type.type)) {
171+
const setType = type.type as ts.TypeReference;
172+
if (!isPrimitiveType(setType.typeArguments![0])) {
173+
throw new Error('only Set<Primitive> maps are supported, extend if needed!');
174+
}
175+
176+
let serializeBlock: ts.Block;
177+
if (isPrimitiveToJson(setType.typeArguments![1], typeChecker)) {
178+
serializeBlock = createNodeFromSource<ts.Block>(
179+
`{
180+
const a:unknown[] = [];
181+
o.set(${JSON.stringify(jsonName)}, a);
182+
for(const v of obj.${fieldName}!) {
183+
a.push(v);
184+
}
185+
}`,
186+
ts.SyntaxKind.Block
187+
);
188+
} else if (isEnumType(setType.typeArguments![0])) {
189+
serializeBlock = createNodeFromSource<ts.Block>(
190+
`{
191+
const a:number[] = [];
192+
o.set(${JSON.stringify(jsonName)}, a);
193+
for(const v of obj.${fieldName}!) {
194+
a.push(v as number);
195+
}
196+
}`,
197+
ts.SyntaxKind.Block
198+
);
199+
} else {
200+
throw new Error('only Set<Primitive> maps are supported, extend if needed!');
195201
}
196202

197203
if (type.isNullable) {
198-
propertyStatements.push(ts.factory.createIfStatement(
199-
ts.factory.createBinaryExpression(
200-
ts.factory.createPropertyAccessExpression(
201-
ts.factory.createIdentifier('obj'),
202-
fieldName
204+
propertyStatements.push(
205+
ts.factory.createIfStatement(
206+
ts.factory.createBinaryExpression(
207+
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName),
208+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
209+
ts.factory.createNull()
203210
),
204-
ts.SyntaxKind.ExclamationEqualsEqualsToken,
205-
ts.factory.createNull()
206-
),
207-
serializeBlock)
211+
serializeBlock
212+
)
208213
);
209214
} else {
210215
propertyStatements.push(serializeBlock);
@@ -242,13 +247,12 @@ function generateToJsonBody(
242247
statements.push(...propertyStatements);
243248
}
244249

245-
if(serializable.hasToJsonExtension) {
246-
statements.push( createNodeFromSource<ts.ExpressionStatement>(
247-
`obj.toJson(o);`,
248-
ts.SyntaxKind.ExpressionStatement
249-
));
250+
if (serializable.hasToJsonExtension) {
251+
statements.push(
252+
createNodeFromSource<ts.ExpressionStatement>(`obj.toJson(o);`, ts.SyntaxKind.ExpressionStatement)
253+
);
250254
}
251-
255+
252256
statements.push(ts.factory.createReturnStatement(ts.factory.createIdentifier('o')));
253257

254258
return ts.factory.createBlock(statements, true);

src.csharp/AlphaTab.Test/Test/Globals.cs

+12
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ public void Length(int length)
133133
}
134134
}
135135

136+
public void Contain(object element)
137+
{
138+
if(_actual is ICollection collection)
139+
{
140+
CollectionAssert.Contains(collection, element);
141+
}
142+
else
143+
{
144+
Assert.Fail("Contain can only be used with collection operands");
145+
}
146+
}
147+
136148
public void True()
137149
{
138150
if (_actual is bool b)

src.csharp/AlphaTab/Core/EcmaScript/Set.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace AlphaTab.Core.EcmaScript;
77

8-
public class Set<T> : IEnumerable<T>
8+
public class Set<T> : IEnumerable<T>, ICollection
99
{
1010
private readonly HashSet<T> _data;
1111

@@ -61,4 +61,12 @@ public void Delete(T item)
6161
{
6262
_data.Remove(item);
6363
}
64+
65+
int ICollection.Count => _data.Count;
66+
bool ICollection.IsSynchronized => false;
67+
object ICollection.SyncRoot => _data;
68+
void ICollection.CopyTo(System.Array array, int index)
69+
{
70+
_data.CopyTo((T[])array, index);
71+
}
6472
}

0 commit comments

Comments
 (0)