-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: compilation error when nested specialization of generic types #145
Conversation
I am preparing to write a detailed documentation and will submit it when completed. |
That would be great! Looking forward to it! |
0a67d7b
to
ec8d1bb
Compare
Signed-off-by: ganjing <[email protected]>
src/utils.ts
Outdated
const typeArgumentsSignature: Array<string> = []; | ||
const _typeParameters = genericClassType.typeArguments; | ||
if (_typeParameters) { | ||
_typeParameters.forEach((type) => { | ||
const index = typeParameters.findIndex((t) => { | ||
return t.name === type.name; | ||
}); | ||
if (index == -1) { | ||
throw new UnimplementError( | ||
`${type.name} not found in typeParameters`, | ||
); | ||
} | ||
typeArgumentsSignature.push(`${typeArguments[index].kind}`); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that these codes are aimed to get the typeArgumentsSignature
, and used in several functions, why not encapsulate these into a separate function(like getTypeSignatureByTypeArguments
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
const typeSignature = | ||
typeArgumentsSignature.length > 0 | ||
? '<' + typeArgumentsSignature.join(',') + '>' | ||
: ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And typeArgumentsSignature
is only used to get the typeSignature
string, maybe we can put this operation in the separate function(getTypeSignatureByTypeArguments
), and use the typeSignature
as return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
const cache = TypeResolver.specializedTypeCache.get(genericOwner); | ||
let found: Type | undefined; | ||
if (cache) { | ||
cache.forEach((v) => { | ||
if (v.has(genericClassType.className + typeSignature)) { | ||
found = v.get(genericClassType.className + typeSignature); | ||
} | ||
}); | ||
} | ||
if (found) return found as TSClass; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This operation seems is used in several functions too,
Maybe we can set a separate function:
function findTypeInSpecialiezedTypeCache(typeName: string): Type {
xxx
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
const reverse = genericMangledName.split('|').reverse(); | ||
reverse[0] = specializedName; | ||
newClassType.mangledName = reverse.reverse().join('|'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These operations seems a little complex, how about
newClassType.mangledName = genericMangledName.substring(0, genericMangledName.lastIndexOf("|") + 1) + specializedName;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
// update specializedTypeCache | ||
if (TypeResolver.specializedTypeCache.has(genericOwner)) { | ||
const value = new Map(); | ||
value.set(specializedName, newClassType); | ||
TypeResolver.specializedTypeCache.get(genericOwner)!.push(value); | ||
} else { | ||
const value = new Map(); | ||
value.set(specializedName, newClassType); | ||
TypeResolver.specializedTypeCache.set(genericOwner, [value]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the operation update specializedTypeCache
can be put in a function too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
// specialized function type need to reset belongedClass and belongedScope | ||
newFuncType.belongedClass = undefined; | ||
newFuncType.setBelongedScope(undefined); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this function, the belongedClass
and belongedScope
are both be set to undefined, where are they set to new values?
What's more, for function, why does it have belongedClass
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- In
processGenericType()
, if it is processing a specialization of a generic,genericTypeSpecialization()
will be called first to generate a new specialized type and its corresponding Scope will first be set to undefined, as marked by this comment. ThecreateScopeBySpecializedType()
will then be called to create a new Scope. - For the default function, there is no belongedClass property, and the code
newFuncType.belongedClass = undefined;
is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
return newFuncType; | ||
} | ||
|
||
export function genericClassMethodSpecialization( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is only slightly different from genericFunctionSpecialization
. Is it possible to merge them into one function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible to merge them together. genericMethodSpecialization
will have some logic for processing Class. I encapsulated them separately, just to distinguish method and function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, how about use a parameter to distinguish method and function, like isMethod
, and set the default value to true
or false
, so that we can reuse the common logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
parent: Scope, | ||
classType: TSClass, | ||
newName?: string, | ||
export function genericMethodSpecialization( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is aim to generate functionScope and set its type for class' methods, right?
The other genericXXSpecialization
functions are aim to generate specialization types, which may be cause some misunderstandings, so this function can rename as other names, like methodSpecialize
etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this function is designed for the following situation:
class A {
a: number;
echo<T>(param: T) {...};
}
const a = new A();
a.echo(1);
a.echo('hello');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
* @param context the parser context | ||
* @returns a new generic type | ||
*/ | ||
export function genericTypeTransformation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between genericTypeSpecialization
and genericTypeTransformation
?
When does genericTypeSpecialization
need to be called, and when does genericTypeTransformation
need to be called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Generic specialization refers to using specific types (such as number, string) to specialize the generic T. In order to realize this feature, we designed the
genericTypeSpecialization()
. - Generics have chain relationships:
class A<T> {...}
class B<X, Y> extends A<Y> {...}
In this case, we need to generate a new generic type B_A<Y>
, and its specialization operation will not affect its original type A<T>
. genericTypeTransformation()
handles this situation.
In the function processGenericType
, we can decide whether to perform specialization of the generic type or create a new generic type by whether typeArguments is a generic types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think there is not much difference between genericTypeSpecialization
and genericTypeTransformation
. Their logic is exactly the same, and their inner logic, like genericFunctionSpecialization
and genericFuncTransformation
is close too.
In genericFunctionSpecialization
:
- get the new type list
- find if there already exists the specialized type, if found, return
- generate new type
- do sepecialization
- return the new type
In genericFuncTransformation
:
- get the new type list
- generate new type
- return the new type
We can use a parameter doSpecialization
to choose which action we require, the codes existed now is much duplicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* @param context the parser context | ||
* @returns a new specialized FunctionScope | ||
*/ | ||
function createFunctionScope( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is not big difference between createFunctionScope
and createMethodScope
, can we use a common function to represent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible to merge them together. createMethodScope
will have some logic for processing Class. I encapsulated them separately, just to distinguish method and function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and how about merge them, and use a parameter to distinguish them? since their inner logic is very close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/type.ts
Outdated
baseClassType = genericClassTransformation( | ||
baseClassType, | ||
typeArguments, | ||
baseClassType.typeArguments!, | ||
this.parserCtx, | ||
classType.className, | ||
) as TSClass; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use processGenericType
?
I see that genericClassTransformation
is used in processGenericType
, in this situation, if invoke processGenericType
directly, can it go to genericClassTransformation
path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should call processGenericType
directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/expression.ts
Outdated
res.setExprType(this.typeResolver.generateNodeType(node)); | ||
const newSuperExpression = new SuperExpression(args); | ||
newSuperExpression.tsNode = ( | ||
expr as SuperExpression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the outer function visitNode
, the tsNode
will be recorded in the expression, so I think there is no need to record the tsNode here again.
Wasmnizer-ts/src/expression.ts
Lines 489 to 493 in 0fb80b5
visitNode(node: ts.Node): Expression { | |
const expr = this.visitNodeInternal(node); | |
expr.tsNode = node; | |
return expr; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/expression.ts
Outdated
).propertyAccessExpr, | ||
newPropertyIdentifier, | ||
); | ||
expr.tsNode = tsNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/expression.ts
Outdated
); | ||
res = classType.getMethod(newPropertyName); | ||
expr.tsNode = tsNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/expression.ts
Outdated
) | ||
: exprType; | ||
newSuperExpression.setExprType(newExprType); | ||
newSuperExpression.tsNode = superExpression.tsNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/utils.ts
Outdated
return newFuncType; | ||
} | ||
|
||
export function genericClassMethodSpecialization( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, how about use a parameter to distinguish method and function, like isMethod
, and set the default value to true
or false
, so that we can reuse the common logic.
src/utils.ts
Outdated
* @param context the parser context | ||
* @returns a new generic type | ||
*/ | ||
export function genericTypeTransformation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think there is not much difference between genericTypeSpecialization
and genericTypeTransformation
. Their logic is exactly the same, and their inner logic, like genericFunctionSpecialization
and genericFuncTransformation
is close too.
In genericFunctionSpecialization
:
- get the new type list
- find if there already exists the specialized type, if found, return
- generate new type
- do sepecialization
- return the new type
In genericFuncTransformation
:
- get the new type list
- generate new type
- return the new type
We can use a parameter doSpecialization
to choose which action we require, the codes existed now is much duplicated.
* @param context the parser context | ||
* @returns a new specialized FunctionScope | ||
*/ | ||
function createFunctionScope( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and how about merge them, and use a parameter to distinguish them? since their inner logic is very close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks @Shanks0224 and @yviansu |
No description provided.