mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-21 18:51:43 +00:00
Move some numeric builtins to stdlib; Minor refactoring
This commit is contained in:
16
src/ast.ts
16
src/ast.ts
@ -1767,23 +1767,17 @@ export class WhileStatement extends Statement {
|
||||
statement: Statement;
|
||||
}
|
||||
|
||||
/** Gets the first decorator by name within at set of decorators, if present. */
|
||||
export function getFirstDecorator(name: string, decorators: DecoratorNode[] | null): DecoratorNode | null {
|
||||
/** Tests if a specific decorator is present within the specified decorators. */
|
||||
export function hasDecorator(name: string, decorators: DecoratorNode[] | null): bool {
|
||||
if (decorators) {
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
let decorator = decorators[i];
|
||||
let expression = decorator.name;
|
||||
let expression = decorators[i].name;
|
||||
if (expression.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>expression).text == name) {
|
||||
return decorator;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Tests if a specific decorator is present within the specified decorators. */
|
||||
export function hasDecorator(name: string, decorators: DecoratorNode[] | null): bool {
|
||||
return getFirstDecorator(name, decorators) != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Mangles a declaration's name to an internal name. */
|
||||
|
153
src/builtins.ts
153
src/builtins.ts
@ -54,22 +54,6 @@ export function compileGetConstant(
|
||||
reportNode: Node
|
||||
): ExpressionRef {
|
||||
switch (global.internalName) {
|
||||
case "NaN": { // context-sensitive
|
||||
if (compiler.currentType == Type.f32) {
|
||||
return compiler.module.createF32(NaN);
|
||||
} else {
|
||||
compiler.currentType = Type.f64;
|
||||
return compiler.module.createF64(NaN);
|
||||
}
|
||||
}
|
||||
case "Infinity": { // context-sensitive
|
||||
if (compiler.currentType == Type.f32) {
|
||||
return compiler.module.createF32(Infinity);
|
||||
} else {
|
||||
compiler.currentType = Type.f64;
|
||||
return compiler.module.createF64(Infinity);
|
||||
}
|
||||
}
|
||||
case "HEAP_BASE": { // never inlined for linking purposes
|
||||
compiler.currentType = compiler.options.usizeType;
|
||||
return compiler.module.createGetGlobal("HEAP_BASE", compiler.currentType.toNativeType());
|
||||
@ -154,143 +138,6 @@ export function compileCall(
|
||||
|
||||
// math
|
||||
|
||||
case "isNaN": { // isNaN<T?>(value: T) -> bool
|
||||
compiler.currentType = Type.bool;
|
||||
if (operands.length != 1) {
|
||||
if (typeArguments && typeArguments.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
}
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (typeArguments) {
|
||||
if (typeArguments.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
|
||||
} else {
|
||||
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
|
||||
}
|
||||
|
||||
switch (compiler.currentType.kind) {
|
||||
case TypeKind.F32: {
|
||||
ret = module.createBinary(
|
||||
BinaryOp.GtU32,
|
||||
module.createBinary(
|
||||
BinaryOp.AndI32,
|
||||
module.createUnary(UnaryOp.ReinterpretF32, arg0),
|
||||
module.createI32(0x7FFFFFFF)
|
||||
),
|
||||
module.createI32(0x7F800000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TypeKind.F64: {
|
||||
ret = module.createBinary(
|
||||
BinaryOp.GtU64,
|
||||
module.createBinary(
|
||||
BinaryOp.AndI64,
|
||||
module.createUnary(UnaryOp.ReinterpretF64, arg0),
|
||||
module.createI64(0xFFFFFFFF, 0x7FFFFFFF)
|
||||
),
|
||||
module.createI64(0, 0x7FF00000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TypeKind.VOID: {
|
||||
compiler.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
reportNode.range
|
||||
);
|
||||
ret = module.createUnreachable();
|
||||
break;
|
||||
}
|
||||
default: { // every other type is never NaN
|
||||
ret = module.createI32(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
compiler.currentType = Type.bool;
|
||||
return ret;
|
||||
}
|
||||
case "isFinite": { // isFinite<T?>(value: T) -> bool
|
||||
compiler.currentType = Type.bool;
|
||||
if (operands.length != 1) {
|
||||
if (typeArguments && typeArguments.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
}
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (typeArguments) {
|
||||
if (typeArguments.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
|
||||
} else {
|
||||
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
|
||||
}
|
||||
switch (compiler.currentType.kind) {
|
||||
case TypeKind.F32: {
|
||||
ret = module.createBinary(
|
||||
BinaryOp.LtU32,
|
||||
module.createBinary(
|
||||
BinaryOp.AndI32,
|
||||
module.createUnary(UnaryOp.ReinterpretF32, arg0),
|
||||
module.createI32(0x7FFFFFFF)
|
||||
),
|
||||
module.createI32(0x7F800000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TypeKind.F64: {
|
||||
ret = module.createBinary(
|
||||
BinaryOp.LtU64,
|
||||
module.createBinary(
|
||||
BinaryOp.AndI64,
|
||||
module.createUnary(UnaryOp.ReinterpretF64, arg0),
|
||||
module.createI64(0xFFFFFFFF, 0x7FFFFFFF)
|
||||
),
|
||||
module.createI64(0, 0x7FF00000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TypeKind.VOID: {
|
||||
compiler.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
reportNode.range
|
||||
);
|
||||
ret = module.createUnreachable();
|
||||
break;
|
||||
}
|
||||
default: { // every other type is always finite
|
||||
ret = module.createI32(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
compiler.currentType = Type.bool;
|
||||
return ret;
|
||||
}
|
||||
case "clz": { // clz<T?>(value: T) -> T
|
||||
if (operands.length != 1) {
|
||||
if (typeArguments) {
|
||||
|
@ -266,12 +266,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.startFunction = startFunctionInstance;
|
||||
this.currentFunction = startFunctionInstance;
|
||||
|
||||
// compile entry file(s) while traversing to reachable elements
|
||||
// compile entry file(s) while traversing reachable elements
|
||||
var sources = program.sources;
|
||||
for (let i = 0, k = sources.length; i < k; ++i) {
|
||||
if (sources[i].isEntry) {
|
||||
this.compileSource(sources[i]);
|
||||
}
|
||||
if (sources[i].isEntry) this.compileSource(sources[i]);
|
||||
}
|
||||
|
||||
// compile the start function if not empty
|
||||
@ -326,7 +324,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
// import memory if requested
|
||||
// import memory if requested (default memory is named '0' by Binaryen)
|
||||
if (options.importMemory) module.addMemoryImport("0", "env", "memory");
|
||||
|
||||
// set up function table
|
||||
@ -343,7 +341,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
functionTableExported = true;
|
||||
}
|
||||
|
||||
// import table if requested
|
||||
// import table if requested (default table is named '0' by Binaryen)
|
||||
if (options.importTable) {
|
||||
module.addTableImport("0", "env", "table");
|
||||
if (!functionTableExported) module.addTableExport("0", "table");
|
||||
@ -354,6 +352,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// sources
|
||||
|
||||
/** Compiles a source by looking it up by path first. */
|
||||
compileSourceByPath(normalizedPathWithoutExtension: string, reportNode: Node): void {
|
||||
var source = this.program.lookupSourceByPath(normalizedPathWithoutExtension);
|
||||
if (!source) {
|
||||
@ -366,8 +365,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.compileSource(source);
|
||||
}
|
||||
|
||||
/** Compiles a source. */
|
||||
compileSource(source: Source): void {
|
||||
if (source.is(CommonFlags.COMPILED)) return;
|
||||
if (source.is(CommonFlags.COMPILED)) return;
|
||||
source.set(CommonFlags.COMPILED);
|
||||
|
||||
// compile top-level statements
|
||||
@ -2098,12 +2098,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
)
|
||||
: this.module.createI64(0);
|
||||
}
|
||||
case TypeKind.F64: {
|
||||
if (!(element.is(CommonFlags.BUILTIN) && contextualType == Type.f32)) {
|
||||
return this.module.createF64((<VariableLikeElement>element).constantFloatValue);
|
||||
}
|
||||
// otherwise fall-through: basically precomputes f32.demote/f64 of NaN / Infinity
|
||||
this.currentType = Type.f32;
|
||||
}
|
||||
case TypeKind.F32: {
|
||||
return this.module.createF32((<VariableLikeElement>element).constantFloatValue);
|
||||
}
|
||||
case TypeKind.F64: {
|
||||
return this.module.createF64((<VariableLikeElement>element).constantFloatValue);
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
return this.module.createUnreachable();
|
||||
@ -2324,18 +2328,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
expr = module.createUnary(UnaryOp.TruncF32ToI64, expr);
|
||||
} else {
|
||||
expr = module.createUnary(UnaryOp.TruncF32ToI32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
} else {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = module.createUnary(UnaryOp.TruncF32ToU64, expr);
|
||||
} else {
|
||||
expr = module.createUnary(UnaryOp.TruncF32ToU32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2346,18 +2346,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
expr = module.createUnary(UnaryOp.TruncF64ToI64, expr);
|
||||
} else {
|
||||
expr = module.createUnary(UnaryOp.TruncF64ToI32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
} else {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = module.createUnary(UnaryOp.TruncF64ToU64, expr);
|
||||
} else {
|
||||
expr = module.createUnary(UnaryOp.TruncF64ToU32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2415,9 +2411,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// i64 to i32
|
||||
if (!toType.is(TypeFlags.LONG)) {
|
||||
expr = module.createUnary(UnaryOp.WrapI64, expr); // discards upper bits
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module);
|
||||
}
|
||||
|
||||
// i32 to i64
|
||||
@ -2426,7 +2420,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// i32 or smaller to even smaller or same size int with change of sign
|
||||
} else if (
|
||||
toType.is(TypeFlags.SMALL) &&
|
||||
toType.is(TypeFlags.SHORT) &&
|
||||
(
|
||||
fromType.size > toType.size ||
|
||||
(
|
||||
@ -4097,7 +4091,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
leftExpr = module.createTeeLocal(tempLocal.index, leftExpr);
|
||||
}
|
||||
|
||||
possiblyOverflows = this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER);
|
||||
possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER);
|
||||
condExpr = makeIsTrueish(leftExpr, this.currentType, module);
|
||||
|
||||
// simplify when cloning left without side effects was successful
|
||||
@ -4143,7 +4137,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
leftExpr = module.createTeeLocal(tempLocal.index, leftExpr);
|
||||
}
|
||||
|
||||
possiblyOverflows = this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER); // if right did
|
||||
possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if right did
|
||||
condExpr = makeIsTrueish(leftExpr, this.currentType, module);
|
||||
|
||||
// simplify when cloning left without side effects was successful
|
||||
@ -4179,7 +4173,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
if (possiblyOverflows && wrapSmallIntegers) {
|
||||
assert(this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER)); // must be a small int
|
||||
assert(this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER)); // must be a small int
|
||||
expr = makeSmallIntegerWrap(expr, this.currentType, module);
|
||||
}
|
||||
return compound
|
||||
@ -6201,7 +6195,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
if (possiblyOverflows) {
|
||||
assert(currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER));
|
||||
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
|
||||
setValue = makeSmallIntegerWrap(setValue, currentType, module);
|
||||
}
|
||||
|
||||
@ -6252,7 +6246,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
false // wrapped below
|
||||
);
|
||||
currentType = this.currentType;
|
||||
possiblyOverflows = currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER); // if operand already did
|
||||
possiblyOverflows = currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if operand already did
|
||||
break;
|
||||
}
|
||||
case Token.MINUS: {
|
||||
@ -6553,7 +6547,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
if (possiblyOverflows && wrapSmallIntegers) {
|
||||
assert(currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER));
|
||||
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
|
||||
expr = makeSmallIntegerWrap(expr, currentType, module);
|
||||
}
|
||||
return compound
|
||||
|
@ -36,17 +36,27 @@ abstract class ExportsWalker {
|
||||
|
||||
/** Program reference. */
|
||||
program: Program;
|
||||
/** Whether to include private members */
|
||||
private includePrivate: bool;
|
||||
/** Already seen elements. */
|
||||
private seen: Set<Element> = new Set();
|
||||
|
||||
/** Constructs a new Element walker. */
|
||||
constructor(program: Program) {
|
||||
constructor(program: Program, includePrivate: bool = false) {
|
||||
this.program = program;
|
||||
this.includePrivate;
|
||||
}
|
||||
|
||||
/** Walks all exports and calls the respective handlers. */
|
||||
walk(): void {
|
||||
for (let element of this.program.moduleLevelExports.values()) this.visitElement(element);
|
||||
}
|
||||
|
||||
/** Visits an element.*/
|
||||
visitElement(element: Element): void {
|
||||
if (element.is(CommonFlags.PRIVATE) && !this.includePrivate) return;
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
switch (element.kind) {
|
||||
case ElementKind.GLOBAL: {
|
||||
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(<Global>element);
|
||||
@ -57,11 +67,11 @@ abstract class ExportsWalker {
|
||||
break;
|
||||
}
|
||||
case ElementKind.FUNCTION_PROTOTYPE: {
|
||||
this.visitCompiledFunctions(<FunctionPrototype>element);
|
||||
this.visitFunctionInstances(<FunctionPrototype>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS_PROTOTYPE: {
|
||||
this.visitCompiledClasses(<ClassPrototype>element);
|
||||
this.visitClassInstances(<ClassPrototype>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
@ -71,26 +81,26 @@ abstract class ExportsWalker {
|
||||
case ElementKind.PROPERTY: {
|
||||
let prop = <Property>element;
|
||||
let getter = prop.getterPrototype;
|
||||
if (getter) this.visitCompiledFunctions(getter);
|
||||
if (getter) this.visitFunctionInstances(getter);
|
||||
let setter = prop.setterPrototype;
|
||||
if (setter) this.visitCompiledFunctions(setter);
|
||||
if (setter) this.visitFunctionInstances(setter);
|
||||
break;
|
||||
}
|
||||
case ElementKind.NAMESPACE: {
|
||||
if (hasCompiledMember(<Namespace>element)) this.visitNamespace(<Namespace>element);
|
||||
if (hasCompiledMember(element)) this.visitNamespace(element);
|
||||
break;
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
visitCompiledFunctions(element: FunctionPrototype): void {
|
||||
private visitFunctionInstances(element: FunctionPrototype): void {
|
||||
for (let instance of element.instances.values()) {
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(<Function>instance);
|
||||
}
|
||||
}
|
||||
|
||||
visitCompiledClasses(element: ClassPrototype): void {
|
||||
private visitClassInstances(element: ClassPrototype): void {
|
||||
for (let instance of element.instances.values()) {
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitClass(<Class>instance);
|
||||
}
|
||||
@ -114,17 +124,14 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
private sb: string[] = [];
|
||||
private seen: Set<Element> = new Set();
|
||||
private indentLevel: i32 = 0;
|
||||
|
||||
/** Constructs a new WebIDL builder. */
|
||||
constructor(program: Program) {
|
||||
super(program);
|
||||
constructor(program: Program, includePrivate: bool = false) {
|
||||
super(program, includePrivate);
|
||||
}
|
||||
|
||||
visitGlobal(element: Global): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
var isConst = element.is(CommonFlags.INLINED);
|
||||
indent(sb, this.indentLevel);
|
||||
@ -151,8 +158,6 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitEnum(element: Enum): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
@ -184,8 +189,6 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitFunction(element: Function): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
var signature = element.signature;
|
||||
indent(sb, this.indentLevel);
|
||||
@ -217,8 +220,6 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitClass(element: Class): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
@ -238,8 +239,6 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitNamespace(element: Namespace): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
@ -298,17 +297,14 @@ export class TSDBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
private sb: string[] = [];
|
||||
private seen: Set<Element> = new Set();
|
||||
private indentLevel: i32 = 0;
|
||||
|
||||
/** Constructs a new WebIDL builder. */
|
||||
constructor(program: Program) {
|
||||
super(program);
|
||||
constructor(program: Program, includePrivate: bool = false) {
|
||||
super(program, includePrivate);
|
||||
}
|
||||
|
||||
visitGlobal(element: Global): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
var isConst = element.is(CommonFlags.INLINED);
|
||||
indent(sb, this.indentLevel);
|
||||
@ -327,8 +323,6 @@ export class TSDBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitEnum(element: Enum): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("enum ");
|
||||
@ -339,7 +333,6 @@ export class TSDBuilder extends ExportsWalker {
|
||||
let numMembers = members.size;
|
||||
for (let [name, member] of members) {
|
||||
if (member.kind == ElementKind.ENUMVALUE) {
|
||||
this.seen.add(member);
|
||||
indent(sb, this.indentLevel);
|
||||
sb.push(name);
|
||||
if (member.is(CommonFlags.INLINED)) {
|
||||
@ -357,8 +350,6 @@ export class TSDBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitFunction(element: Function): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
if (element.is(CommonFlags.PRIVATE)) return;
|
||||
var sb = this.sb;
|
||||
var signature = element.signature;
|
||||
@ -397,8 +388,6 @@ export class TSDBuilder extends ExportsWalker {
|
||||
}
|
||||
|
||||
visitClass(element: Class): void {
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var sb = this.sb;
|
||||
var isInterface = element.kind == ElementKind.INTERFACE;
|
||||
indent(sb, this.indentLevel++);
|
||||
@ -530,8 +519,6 @@ export class TSDBuilder extends ExportsWalker {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: C bindings? or is this sufficiently covered by WebIDL and using a 3rd-party tool?
|
||||
|
||||
// helpers
|
||||
|
||||
/** Tests if a namespace-like element has at least one compiled member. */
|
||||
|
@ -2600,7 +2600,7 @@ export class FunctionPrototype extends Element {
|
||||
if (isInstance) {
|
||||
classInstance = assert(classPrototype).resolve(classTypeArguments, contextualTypeArguments); // reports
|
||||
if (!classInstance) return null;
|
||||
thisType = classInstance.type.asThis();
|
||||
thisType = classInstance.type;
|
||||
contextualTypeArguments.set("this", thisType);
|
||||
}
|
||||
|
||||
|
125
src/types.ts
125
src/types.ts
@ -69,20 +69,18 @@ export const enum TypeFlags {
|
||||
INTEGER = 1 << 2,
|
||||
/** Is a floating point type. */
|
||||
FLOAT = 1 << 3,
|
||||
/** Is a sized integer type with a target specific bit size. */
|
||||
SIZE = 1 << 4,
|
||||
/** Is a small type that is emulated in a larger type. */
|
||||
SMALL = 1 << 5,
|
||||
/** Is a long type larger than 32-bits. */
|
||||
/** Is a pointer type. */
|
||||
POINTER = 1 << 4,
|
||||
/** Is smaller than 32-bits. */
|
||||
SHORT = 1 << 5,
|
||||
/** Is larger than 32-bits. */
|
||||
LONG = 1 << 6,
|
||||
/** Is a value type. */
|
||||
VALUE = 1 << 7,
|
||||
/** Is a reference type. */
|
||||
REFERENCE = 1 << 8,
|
||||
/** Is a nullable type. */
|
||||
NULLABLE = 1 << 9,
|
||||
/** Is the special 'this' type. */
|
||||
THIS = 1 << 10
|
||||
NULLABLE = 1 << 9
|
||||
}
|
||||
|
||||
/** Represents a resolved type. */
|
||||
@ -94,21 +92,19 @@ export class Type {
|
||||
flags: TypeFlags;
|
||||
/** Size in bits. */
|
||||
size: u32;
|
||||
/** Size in bytes. Ceiled to 8-bits. */
|
||||
/** Size in bytes. */
|
||||
byteSize: i32;
|
||||
/** Underlying class reference, if a class type. */
|
||||
classReference: Class | null;
|
||||
/** Underlying function reference, if a function type. */
|
||||
/** Underlying signature reference, if a function type. */
|
||||
signatureReference: Signature | null;
|
||||
/** Respective nullable type, if non-nullable. */
|
||||
nullableType: Type | null = null;
|
||||
/** Respective non-nullable type, if nullable. */
|
||||
nonNullableType: Type;
|
||||
/** Respective special 'this' type. */
|
||||
thisType: Type | null = null;
|
||||
/** Cached nullable type, if non-nullable. */
|
||||
private cachedNullableType: Type | null = null;
|
||||
|
||||
/** Constructs a new resolved type. */
|
||||
constructor(kind: TypeKind, flags: TypeFlags, size: i32) {
|
||||
constructor(kind: TypeKind, flags: TypeFlags, size: u32) {
|
||||
this.kind = kind;
|
||||
this.flags = flags;
|
||||
this.size = size;
|
||||
@ -128,7 +124,7 @@ export class Type {
|
||||
return ~0 >>> (targetType.size - this.size);
|
||||
}
|
||||
|
||||
/** Tests if this type has the specified flags. */
|
||||
/** Tests if this type has (all of) the specified flags. */
|
||||
is(flags: TypeFlags): bool { return (this.flags & flags) == flags; }
|
||||
/** Tests if this type has any of the specified flags. */
|
||||
isAny(flags: TypeFlags): bool { return (this.flags & flags) != 0; }
|
||||
@ -152,25 +148,13 @@ export class Type {
|
||||
/** Composes the respective nullable type of this type. */
|
||||
asNullable(): Type {
|
||||
assert(this.is(TypeFlags.REFERENCE));
|
||||
if (!this.nullableType) {
|
||||
if (!this.cachedNullableType) {
|
||||
assert(!this.is(TypeFlags.NULLABLE));
|
||||
this.nullableType = new Type(this.kind, this.flags | TypeFlags.NULLABLE, this.size);
|
||||
this.nullableType.classReference = this.classReference; // either a class reference
|
||||
this.nullableType.signatureReference = this.signatureReference; // or a function reference
|
||||
this.cachedNullableType = new Type(this.kind, this.flags | TypeFlags.NULLABLE, this.size);
|
||||
this.cachedNullableType.classReference = this.classReference; // either a class reference
|
||||
this.cachedNullableType.signatureReference = this.signatureReference; // or a function reference
|
||||
}
|
||||
return this.nullableType;
|
||||
}
|
||||
|
||||
/** Composes the respective 'this' type of this type. */
|
||||
asThis(): Type {
|
||||
var thisType = this.thisType;
|
||||
if (thisType) return thisType;
|
||||
thisType = new Type(this.kind, this.flags | TypeFlags.THIS, this.size);
|
||||
thisType.classReference = this.classReference;
|
||||
thisType.nullableType = this.nullableType;
|
||||
thisType.nonNullableType = this.nonNullableType;
|
||||
this.thisType = thisType;
|
||||
return thisType;
|
||||
return this.cachedNullableType;
|
||||
}
|
||||
|
||||
/** Tests if a value of this type is assignable to a target of the specified type. */
|
||||
@ -213,11 +197,8 @@ export class Type {
|
||||
|
||||
/** Determines the common compatible type of two types, if any. */
|
||||
static commonCompatible(left: Type, right: Type, signednessIsImportant: bool): Type | null {
|
||||
if (right.isAssignableTo(left, signednessIsImportant)) {
|
||||
return left;
|
||||
} else if (left.isAssignableTo(right, signednessIsImportant)) {
|
||||
return right;
|
||||
}
|
||||
if (right.isAssignableTo(left, signednessIsImportant)) return left;
|
||||
else if (left.isAssignableTo(right, signednessIsImportant)) return right;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -233,16 +214,12 @@ export class Type {
|
||||
case TypeKind.U16: return "u16";
|
||||
case TypeKind.U32: {
|
||||
let functionType = this.signatureReference;
|
||||
return kindOnly || !functionType
|
||||
? "u32"
|
||||
: functionType.toString(true);
|
||||
return kindOnly || !functionType ? "u32" : functionType.toString(true);
|
||||
}
|
||||
case TypeKind.U64: return "u64";
|
||||
case TypeKind.USIZE: {
|
||||
let classType = this.classReference;
|
||||
return kindOnly || !classType
|
||||
? "usize"
|
||||
: classType.toString();
|
||||
return kindOnly || !classType ? "usize" : classType.toString();
|
||||
}
|
||||
case TypeKind.BOOL: return "bool";
|
||||
case TypeKind.F32: return "f32";
|
||||
@ -332,7 +309,7 @@ export class Type {
|
||||
/** An 8-bit signed integer. */
|
||||
static readonly i8: Type = new Type(TypeKind.I8,
|
||||
TypeFlags.SIGNED |
|
||||
TypeFlags.SMALL |
|
||||
TypeFlags.SHORT |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.VALUE, 8
|
||||
);
|
||||
@ -340,7 +317,7 @@ export class Type {
|
||||
/** A 16-bit signed integer. */
|
||||
static readonly i16: Type = new Type(TypeKind.I16,
|
||||
TypeFlags.SIGNED |
|
||||
TypeFlags.SMALL |
|
||||
TypeFlags.SHORT |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.VALUE, 16
|
||||
);
|
||||
@ -363,8 +340,8 @@ export class Type {
|
||||
/** A 32-bit signed size. WASM32 only. */
|
||||
static readonly isize32: Type = new Type(TypeKind.ISIZE,
|
||||
TypeFlags.SIGNED |
|
||||
TypeFlags.SIZE |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.POINTER |
|
||||
TypeFlags.VALUE, 32
|
||||
);
|
||||
|
||||
@ -372,15 +349,15 @@ export class Type {
|
||||
static readonly isize64: Type = new Type(TypeKind.ISIZE,
|
||||
TypeFlags.SIGNED |
|
||||
TypeFlags.LONG |
|
||||
TypeFlags.SIZE |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.POINTER |
|
||||
TypeFlags.VALUE, 64
|
||||
);
|
||||
|
||||
/** An 8-bit unsigned integer. */
|
||||
static readonly u8: Type = new Type(TypeKind.U8,
|
||||
TypeFlags.UNSIGNED |
|
||||
TypeFlags.SMALL |
|
||||
TypeFlags.SHORT |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.VALUE, 8
|
||||
);
|
||||
@ -388,7 +365,7 @@ export class Type {
|
||||
/** A 16-bit unsigned integer. */
|
||||
static readonly u16: Type = new Type(TypeKind.U16,
|
||||
TypeFlags.UNSIGNED |
|
||||
TypeFlags.SMALL |
|
||||
TypeFlags.SHORT |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.VALUE, 16
|
||||
);
|
||||
@ -411,8 +388,8 @@ export class Type {
|
||||
/** A 32-bit unsigned size. WASM32 only. */
|
||||
static readonly usize32: Type = new Type(TypeKind.USIZE,
|
||||
TypeFlags.UNSIGNED |
|
||||
TypeFlags.SIZE |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.POINTER |
|
||||
TypeFlags.VALUE, 32
|
||||
);
|
||||
|
||||
@ -420,15 +397,15 @@ export class Type {
|
||||
static readonly usize64: Type = new Type(TypeKind.USIZE,
|
||||
TypeFlags.UNSIGNED |
|
||||
TypeFlags.LONG |
|
||||
TypeFlags.SIZE |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.POINTER |
|
||||
TypeFlags.VALUE, 64
|
||||
);
|
||||
|
||||
/** A 1-bit unsigned integer. */
|
||||
static readonly bool: Type = new Type(TypeKind.BOOL,
|
||||
TypeFlags.UNSIGNED |
|
||||
TypeFlags.SMALL |
|
||||
TypeFlags.SHORT |
|
||||
TypeFlags.INTEGER |
|
||||
TypeFlags.VALUE, 1
|
||||
);
|
||||
@ -456,9 +433,7 @@ export class Type {
|
||||
export function typesToNativeTypes(types: Type[]): NativeType[] {
|
||||
var numTypes = types.length;
|
||||
var ret = new Array<NativeType>(numTypes);
|
||||
for (let i = 0; i < numTypes; ++i) {
|
||||
ret[i] = types[i].toNativeType();
|
||||
}
|
||||
for (let i = 0; i < numTypes; ++i) ret[i] = types[i].toNativeType();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -467,9 +442,7 @@ export function typesToString(types: Type[]): string {
|
||||
var numTypes = types.length;
|
||||
if (!numTypes) return "";
|
||||
var sb = new Array<string>(numTypes);
|
||||
for (let i = 0; i < numTypes; ++i) {
|
||||
sb[i] = types[i].toString();
|
||||
}
|
||||
for (let i = 0; i < numTypes; ++i) sb[i] = types[i].toString();
|
||||
return sb.join(",");
|
||||
}
|
||||
|
||||
@ -524,31 +497,23 @@ export class Signature {
|
||||
var thisThisType = this.thisType;
|
||||
var targetThisType = target.thisType;
|
||||
if (thisThisType) {
|
||||
if (!(targetThisType && thisThisType.isAssignableTo(targetThisType))) {
|
||||
return false;
|
||||
}
|
||||
if (!(targetThisType && thisThisType.isAssignableTo(targetThisType))) return false;
|
||||
} else if (targetThisType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check rest parameter
|
||||
if (this.hasRest != target.hasRest) {
|
||||
return false; // TODO
|
||||
}
|
||||
if (this.hasRest != target.hasRest) return false; // TODO
|
||||
|
||||
// check parameter types
|
||||
var thisParameterTypes = this.parameterTypes;
|
||||
var targetParameterTypes = target.parameterTypes;
|
||||
var numParameters = thisParameterTypes.length;
|
||||
if (numParameters != targetParameterTypes.length) {
|
||||
return false;
|
||||
}
|
||||
if (numParameters != targetParameterTypes.length) return false;
|
||||
for (let i = 0; i < numParameters; ++i) {
|
||||
let thisParameterType = thisParameterTypes[i];
|
||||
let targetParameterType = targetParameterTypes[i];
|
||||
if (!thisParameterType.isAssignableTo(targetParameterType)) {
|
||||
return false;
|
||||
}
|
||||
if (!thisParameterType.isAssignableTo(targetParameterType)) return false;
|
||||
}
|
||||
|
||||
// check return type
|
||||
@ -562,9 +527,7 @@ export class Signature {
|
||||
var sb = [];
|
||||
if (thisType) sb.push(thisType.toSignatureString());
|
||||
if (parameterTypes) {
|
||||
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
|
||||
sb.push(parameterTypes[i].toSignatureString());
|
||||
}
|
||||
for (let i = 0, k = parameterTypes.length; i < k; ++i) sb.push(parameterTypes[i].toSignatureString());
|
||||
}
|
||||
sb.push(returnType.toSignatureString());
|
||||
return sb.join("");
|
||||
@ -598,16 +561,10 @@ export class Signature {
|
||||
for (let i = 0; i < numParameters; ++i, ++index) {
|
||||
if (index) sb.push(", ");
|
||||
if (i == restIndex) sb.push("...");
|
||||
if (i < numNames) {
|
||||
sb.push((<string[]>names)[i]);
|
||||
} else {
|
||||
sb.push(getGenericParameterName(i));
|
||||
}
|
||||
if (i >= optionalStart && i != restIndex) {
|
||||
sb.push("?: ");
|
||||
} else {
|
||||
sb.push(": ");
|
||||
}
|
||||
if (i < numNames) sb.push((<string[]>names)[i]);
|
||||
else sb.push(getGenericParameterName(i));
|
||||
if (i >= optionalStart && i != restIndex) sb.push("?: ");
|
||||
else sb.push(": ");
|
||||
sb.push(parameters[i].toString());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user