Fix external names of class exports and similar; Support a few more kinds in TSDBuilder, see #74

This commit is contained in:
dcodeIO
2018-04-15 00:34:19 +02:00
parent 78a679759a
commit 81223977d0
15 changed files with 713 additions and 202 deletions

View File

@ -7,7 +7,7 @@ import {
compileCall as compileBuiltinCall,
compileGetConstant as compileBuiltinGetConstant,
compileAllocate as compileBuiltinAllocate,
compileAbort
compileAbort as compileBuiltinAbort
} from "./builtins";
import {
@ -32,6 +32,7 @@ import {
Program,
ClassPrototype,
Class,
Element,
ElementKind,
Enum,
Field,
@ -52,7 +53,11 @@ import {
DecoratorFlags,
PATH_DELIMITER,
INNER_DELIMITER
INNER_DELIMITER,
INSTANCE_DELIMITER,
STATIC_DELIMITER,
GETTER_PREFIX,
SETTER_PREFIX
} from "./program";
import {
@ -272,10 +277,14 @@ export class Compiler extends DiagnosticEmitter {
// compile the start function if not empty
var startFunctionBody = this.startFunctionBody;
if (startFunctionBody.length) {
let typeRef = this.ensureFunctionType(startFunctionInstance.signature);
let signature = startFunctionInstance.signature;
let funcRef = module.addFunction(
startFunctionInstance.internalName,
typeRef,
this.ensureFunctionType(
signature.parameterTypes,
signature.returnType,
signature.thisType
),
typesToNativeTypes(startFunctionInstance.additionalLocals),
module.createBlock(null, startFunctionBody)
);
@ -510,8 +519,8 @@ export class Compiler extends DiagnosticEmitter {
global.set(CommonFlags.MODULE_IMPORT);
module.addGlobalImport(
global.internalName,
global.namespace
? global.namespace.simpleName
global.parent
? global.parent.simpleName
: "env",
global.simpleName,
nativeType
@ -609,21 +618,15 @@ export class Compiler extends DiagnosticEmitter {
DiagnosticCode.Operation_not_supported,
assert(global.declaration).range
);
global.constantValueKind = ConstantValueKind.INTEGER;
global.constantIntegerValue = i64_new(0);
break;
return false;
}
}
global.set(CommonFlags.INLINED); // inline the value from now on
if (declaration) {
if (declaration.isTopLevel) { // but keep the element as it might be re-exported
module.addGlobal(internalName, nativeType, false, initExpr);
}
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
module.addGlobalExport(global.internalName, declaration.programLevelInternalName);
}
} else {
assert(false); // must have a declaration if constant
if (global.is(CommonFlags.MODULE_EXPORT)) {
module.addGlobal(internalName, nativeType, false, initExpr);
module.addGlobalExport(internalName, mangleExportName(global));
} else if (declaration && declaration.isTopLevel) { // might become re-exported
module.addGlobal(internalName, nativeType, false, initExpr);
}
} else /* mutable */ {
@ -726,9 +729,9 @@ export class Compiler extends DiagnosticEmitter {
previousValue = <EnumValue>val;
// export values if the enum is exported
if (element.declaration.range.source.isEntry && element.declaration.isTopLevelExport) {
if (element.is(CommonFlags.MODULE_EXPORT)) {
if (member.is(CommonFlags.INLINED)) {
module.addGlobalExport(member.internalName, member.internalName);
module.addGlobalExport(member.internalName, mangleExportName(member));
} else if (valueDeclaration) {
this.warning(
DiagnosticCode.Cannot_export_a_mutable_global,
@ -781,10 +784,12 @@ export class Compiler extends DiagnosticEmitter {
}
/** Either reuses or creates the function type matching the specified signature. */
private ensureFunctionType(signature: Signature): FunctionTypeRef {
var parameters = signature.parameterTypes;
var numParameters = parameters.length;
var thisType = signature.thisType;
private ensureFunctionType(
parameterTypes: Type[] | null,
returnType: Type,
thisType: Type | null = null
): FunctionTypeRef {
var numParameters = parameterTypes ? parameterTypes.length : 0;
var paramTypes: NativeType[];
var index = 0;
if (thisType) {
@ -794,14 +799,17 @@ export class Compiler extends DiagnosticEmitter {
} else {
paramTypes = new Array(numParameters);
}
for (let i = 0; i < numParameters; ++i, ++index) {
paramTypes[index] = signature.parameterTypes[i].toNativeType();
if (parameterTypes) {
for (let i = 0; i < numParameters; ++i, ++index) {
paramTypes[index] = parameterTypes[i].toNativeType();
}
}
var resultType = signature.returnType.toNativeType();
var resultType = returnType.toNativeType();
var module = this.module;
var typeRef = module.getFunctionTypeBySignature(resultType, paramTypes);
if (!typeRef) {
typeRef = module.addFunctionType(signature.toSignatureString(), resultType, paramTypes);
let name = Signature.makeSignatureString(parameterTypes, returnType, thisType);
typeRef = module.addFunctionType(name, resultType, paramTypes);
}
return typeRef;
}
@ -832,7 +840,8 @@ export class Compiler extends DiagnosticEmitter {
}
var ref: FunctionRef;
var typeRef = this.ensureFunctionType(instance.signature);
var signature = instance.signature;
var typeRef = this.ensureFunctionType(signature.parameterTypes, signature.returnType, signature.thisType);
var module = this.module;
if (body) {
let isConstructor = instance.is(CommonFlags.CONSTRUCTOR);
@ -868,7 +877,7 @@ export class Compiler extends DiagnosticEmitter {
// if not all branches are guaranteed to allocate, also append a conditional allocation
} else {
let parent = assert(instance.memberOf);
let parent = assert(instance.parent);
assert(parent.kind == ElementKind.CLASS);
stmt = module.createBlock(null, [
stmt,
@ -901,11 +910,11 @@ export class Compiler extends DiagnosticEmitter {
instance.set(CommonFlags.MODULE_IMPORT);
// create the function import
let namespace = instance.prototype.namespace;
let parent = instance.prototype.parent;
ref = module.addFunctionImport(
instance.internalName,
namespace
? namespace.simpleName
parent
? parent.simpleName
: "env",
instance.simpleName,
typeRef
@ -913,8 +922,8 @@ export class Compiler extends DiagnosticEmitter {
}
// check module-level export
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
module.addFunctionExport(instance.internalName, declaration.name.text);
if (instance.is(CommonFlags.MODULE_EXPORT)) {
module.addFunctionExport(instance.internalName, mangleExportName(instance));
}
instance.finalize(module, ref);
@ -1143,6 +1152,133 @@ export class Compiler extends DiagnosticEmitter {
compileClass(instance: Class): bool {
if (instance.is(CommonFlags.COMPILED)) return true;
instance.set(CommonFlags.COMPILED);
var staticMembers = instance.prototype.members;
if (staticMembers) {
for (let element of staticMembers.values()) {
switch (element.kind) {
case ElementKind.GLOBAL: {
this.compileGlobal(<Global>element);
break;
}
case ElementKind.FUNCTION_PROTOTYPE: {
if (
!(<FunctionPrototype>element).is(CommonFlags.GENERIC)
) {
this.compileFunctionUsingTypeArguments(
<FunctionPrototype>element,
[], null, null,
(<FunctionPrototype>element).declaration.name
);
}
break;
}
case ElementKind.PROPERTY: {
let getter = (<Property>element).getterPrototype;
if (getter) {
this.compileFunctionUsingTypeArguments(
getter,
[], null, null,
getter.declaration.name
);
}
let setter = (<Property>element).setterPrototype;
if (setter) {
this.compileFunctionUsingTypeArguments(
setter,
[], null, null,
setter.declaration.name
);
}
break;
}
}
}
}
var instanceMembers = instance.members;
if (instanceMembers) {
for (let element of instanceMembers.values()) {
switch (element.kind) {
case ElementKind.FUNCTION_PROTOTYPE: {
if (
!(<FunctionPrototype>element).is(CommonFlags.GENERIC)
) {
this.compileFunctionUsingTypeArguments(
<FunctionPrototype>element,
[],
instance.contextualTypeArguments,
null, // no outer scope
(<FunctionPrototype>element).declaration.name
);
}
break;
}
case ElementKind.FIELD: {
element.set(CommonFlags.COMPILED);
if (!instance.is(CommonFlags.MODULE_EXPORT) || element.is(CommonFlags.PRIVATE)) break;
let module = this.module;
let name = (<Field>element).simpleName;
let type = (<Field>element).type;
let nativeType = type.toNativeType();
let offset = (<Field>element).memoryOffset;
let usizeType = this.options.usizeType;
let nativeSizeType = this.options.nativeSizeType;
// export an implicit getter: get:fieldName(this: usize) -> fieldType
let getterName = mangleExportName(element, GETTER_PREFIX + name);
module.addFunction(
getterName,
this.ensureFunctionType(null, type, usizeType),
null,
module.createLoad(
type.byteSize,
type.is(TypeFlags.SIGNED),
module.createGetLocal(0, nativeSizeType),
nativeType,
offset
)
);
module.addFunctionExport(getterName, getterName);
// export an implicit setter: set:fieldName(this: usize, value: fieldType) -> void
if (element.is(CommonFlags.READONLY)) break;
let setterName = mangleExportName(element, SETTER_PREFIX + name);
module.addFunction(
setterName,
this.ensureFunctionType([ type ], Type.void, usizeType),
null,
module.createStore(
type.byteSize,
module.createGetLocal(0, nativeSizeType),
module.createGetLocal(1, nativeType),
nativeType,
offset
)
);
module.addFunctionExport(setterName, setterName);
break;
}
case ElementKind.PROPERTY: {
let getter = (<Property>element).getterPrototype;
if (getter) {
this.compileFunctionUsingTypeArguments(
getter,
[], instance.contextualTypeArguments, null,
getter.declaration.name
);
}
let setter = (<Property>element).setterPrototype;
if (setter) {
this.compileFunctionUsingTypeArguments(
setter,
[], instance.contextualTypeArguments, null,
setter.declaration.name
);
}
break;
}
}
}
}
return true;
}
@ -1672,7 +1808,7 @@ export class Compiler extends DiagnosticEmitter {
flow.set(FlowFlags.RETURNS);
// TODO: requires exception-handling spec.
return compileAbort(this, null, statement);
return compileBuiltinAbort(this, null, statement);
}
compileTryStatement(statement: TryStatement): ExpressionRef {
@ -4060,9 +4196,9 @@ export class Compiler extends DiagnosticEmitter {
var argumentExpressions: Expression[];
var thisArg: ExpressionRef = 0;
if (operatorInstance.is(CommonFlags.INSTANCE)) {
let classElement = assert(operatorInstance.memberOf);
assert(classElement.kind == ElementKind.CLASS);
thisArg = this.compileExpression(value, (<Class>classElement).type);
let parent = assert(operatorInstance.parent);
assert(parent.kind == ElementKind.CLASS);
thisArg = this.compileExpression(value, (<Class>parent).type);
argumentExpressions = [];
} else {
argumentExpressions = [ value ];
@ -4086,9 +4222,9 @@ export class Compiler extends DiagnosticEmitter {
var argumentExpressions: Expression[];
var thisArg: ExpressionRef = 0;
if (operatorInstance.is(CommonFlags.INSTANCE)) {
let classElement = assert(operatorInstance.memberOf);
assert(classElement.kind == ElementKind.CLASS);
thisArg = this.compileExpression(left, (<Class>classElement).type);
let parent = assert(operatorInstance.parent);
assert(parent.kind == ElementKind.CLASS);
thisArg = this.compileExpression(left, (<Class>parent).type);
argumentExpressions = [ right ];
} else {
argumentExpressions = [ left, right ];
@ -4755,9 +4891,9 @@ export class Compiler extends DiagnosticEmitter {
// here, with their respective locals being blocked. There is no 'makeCallInline'.
var body = [];
if (thisArg) {
let classElement = assert(instance.memberOf);
assert(classElement.kind == ElementKind.CLASS);
let thisLocal = flow.addScopedLocal((<Class>classElement).type, "this");
let parent = assert(instance.parent);
assert(parent.kind == ElementKind.CLASS);
let thisLocal = flow.addScopedLocal((<Class>parent).type, "this");
body.push(
module.createSetLocal(thisLocal.index, thisArg)
);
@ -4879,7 +5015,7 @@ export class Compiler extends DiagnosticEmitter {
original.prototype,
trampolineName,
trampolineSignature,
original.memberOf,
original.parent,
original.contextualTypeArguments
);
trampoline.set(original.flags | CommonFlags.TRAMPOLINE | CommonFlags.COMPILED);
@ -4929,8 +5065,14 @@ export class Compiler extends DiagnosticEmitter {
this.currentFunction = previousFunction;
assert(operandIndex == maxOperands);
var typeRef = this.ensureFunctionType(trampolineSignature);
var funcRef = module.addFunction(trampolineName, typeRef, typesToNativeTypes(trampoline.additionalLocals),
var funcRef = module.addFunction(
trampolineName,
this.ensureFunctionType(
trampolineSignature.parameterTypes,
trampolineSignature.returnType,
trampolineSignature.thisType
),
typesToNativeTypes(trampoline.additionalLocals),
module.createBlock(null, [
body,
module.createCall(
@ -5052,7 +5194,7 @@ export class Compiler extends DiagnosticEmitter {
}
assert(numOperands >= minOperands);
this.ensureFunctionType(signature);
this.ensureFunctionType(signature.parameterTypes, signature.returnType, signature.thisType);
var module = this.module;
// fill up omitted arguments with zeroes
@ -5200,7 +5342,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
if (currentFunction.is(CommonFlags.INSTANCE)) {
let parent = assert(currentFunction.memberOf);
let parent = assert(currentFunction.parent);
assert(parent.kind == ElementKind.CLASS);
let thisType = (<Class>parent).type;
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
@ -5238,7 +5380,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
if (currentFunction.is(CommonFlags.INSTANCE)) {
let parent = assert(currentFunction.memberOf);
let parent = assert(currentFunction.parent);
assert(parent.kind == ElementKind.CLASS);
let base = (<Class>parent).base;
if (base) {
@ -5759,7 +5901,9 @@ export class Compiler extends DiagnosticEmitter {
return module.createGetGlobal((<Global>target).internalName, globalType.toNativeType());
}
case ElementKind.ENUMVALUE: { // enum value
if (!this.compileEnum((<EnumValue>target).enum)) {
let parent = (<EnumValue>target).parent;
assert(parent !== null && parent.kind == ElementKind.ENUM);
if (!this.compileEnum(<Enum>parent)) {
return this.module.createUnreachable();
}
this.currentType = Type.i32;
@ -5777,7 +5921,7 @@ export class Compiler extends DiagnosticEmitter {
);
this.currentType = (<Field>target).type;
return module.createLoad(
(<Field>target).type.size >> 3,
(<Field>target).type.byteSize,
(<Field>target).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
thisExpr,
(<Field>target).type.toNativeType(),
@ -5799,7 +5943,7 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
if (instance.is(CommonFlags.INSTANCE)) {
let parent = assert(instance.memberOf);
let parent = assert(instance.parent);
assert(parent.kind == ElementKind.CLASS);
let thisExpression = assert(program.resolvedThisExpression);
let thisExpr = this.compileExpressionRetainType(
@ -6420,6 +6564,50 @@ export class Compiler extends DiagnosticEmitter {
// helpers
function mangleExportName(element: Element, explicitSimpleName: string | null = null): string {
var simpleName = explicitSimpleName != null
? explicitSimpleName
: element.simpleName;
switch (element.kind) {
case ElementKind.FUNCTION: {
let parent = (<Function>element).parent || (<Function>element).prototype.parent;
return parent
? mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName
: simpleName;
}
case ElementKind.FIELD: {
let parent = assert((<Field>element).parent);
return mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName;
}
case ElementKind.ENUMVALUE: {
let parent = assert((<EnumValue>element).parent);
return mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName;
}
case ElementKind.CLASS: {
let parent = (<Class>element).prototype.parent;
return parent
? mangleExportName(parent)
+ STATIC_DELIMITER
+ simpleName
: simpleName;
}
default: {
let parent = element.parent;
return parent
? mangleExportName(parent)
+ STATIC_DELIMITER
+ simpleName
: simpleName;
}
}
}
/** Adds the debug location of the specified expression at the specified range to the source map. */
function addDebugLocation(expr: ExpressionRef, range: Range, module: Module, currentFunction: Function): void {
var source = range.source;

View File

@ -11,13 +11,15 @@ import {
Global,
Enum,
EnumValue,
Field,
Function,
Class,
Namespace,
FunctionPrototype,
Class,
ClassPrototype,
Namespace,
ConstantValueKind,
Interface
Interface,
Property
} from "./program";
import {
@ -41,51 +43,56 @@ abstract class ExportsWalker {
}
walk(): void {
for (let element of this.program.moduleLevelExports.values()) {
this.visitElement(element);
}
for (let element of this.program.moduleLevelExports.values()) this.visitElement(element);
}
visitElement(element: Element): void {
switch (element.kind) {
case ElementKind.GLOBAL: {
if (element.is(CommonFlags.COMPILED)) {
this.visitGlobal(<Global>element);
}
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(<Global>element);
break;
}
case ElementKind.ENUM: {
if (element.is(CommonFlags.COMPILED)) {
this.visitEnum(<Enum>element);
}
if (element.is(CommonFlags.COMPILED)) this.visitEnum(<Enum>element);
break;
}
case ElementKind.FUNCTION_PROTOTYPE: {
for (let instance of (<FunctionPrototype>element).instances.values()) {
if (instance.is(CommonFlags.COMPILED)) {
this.visitFunction(<Function>instance);
}
}
this.visitCompiledFunctions(<FunctionPrototype>element);
break;
}
case ElementKind.CLASS_PROTOTYPE: {
for (let instance of (<ClassPrototype>element).instances.values()) {
if (instance.is(CommonFlags.COMPILED)) {
this.visitClass(<Class>instance);
}
}
this.visitCompiledClasses(<ClassPrototype>element);
break;
}
case ElementKind.FIELD: {
if ((<Field>element).is(CommonFlags.COMPILED)) this.visitField(<Field>element);
break;
}
case ElementKind.PROPERTY: {
let prop = <Property>element;
let getter = prop.getterPrototype;
if (getter) this.visitCompiledFunctions(getter);
let setter = prop.setterPrototype;
if (setter) this.visitCompiledFunctions(setter);
break;
}
case ElementKind.NAMESPACE: {
if ((<Namespace>element).is(CommonFlags.COMPILED)) {
this.visitNamespace(<Namespace>element);
}
break;
}
default: {
assert(false);
if (hasCompiledMember(<Namespace>element)) this.visitNamespace(<Namespace>element);
break;
}
default: assert(false);
}
}
visitCompiledFunctions(element: FunctionPrototype): void {
for (let instance of element.instances.values()) {
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(<Function>instance);
}
}
visitCompiledClasses(element: ClassPrototype): void {
for (let instance of element.instances.values()) {
if (instance.is(CommonFlags.COMPILED)) this.visitClass(<Class>instance);
}
}
@ -94,6 +101,7 @@ abstract class ExportsWalker {
abstract visitFunction(element: Function): void;
abstract visitClass(element: Class): void;
abstract visitInterface(element: Interface): void;
abstract visitField(element: Field): void;
abstract visitNamespace(element: Element): void;
}
@ -120,9 +128,7 @@ export class IDLBuilder extends ExportsWalker {
var sb = this.sb;
var isConst = element.is(CommonFlags.INLINED);
indent(sb, this.indentLevel);
if (isConst) {
sb.push("const ");
}
if (isConst) sb.push("const ");
sb.push(this.typeToString(element.type));
sb.push(" ");
sb.push(element.simpleName);
@ -158,11 +164,8 @@ export class IDLBuilder extends ExportsWalker {
if (member.kind == ElementKind.ENUMVALUE) {
let isConst = (<EnumValue>member).is(CommonFlags.INLINED);
indent(sb, this.indentLevel);
if (isConst) {
sb.push("const ");
} else {
sb.push("readonly ");
}
if (isConst) sb.push("const ");
else sb.push("readonly ");
sb.push("unsigned long ");
sb.push(name);
if (isConst) {
@ -173,9 +176,7 @@ export class IDLBuilder extends ExportsWalker {
}
}
for (let member of members.values()) {
if (member.kind != ElementKind.ENUMVALUE) {
this.visitElement(member);
}
if (member.kind != ElementKind.ENUMVALUE) this.visitElement(member);
}
}
indent(sb, --this.indentLevel);
@ -209,9 +210,7 @@ export class IDLBuilder extends ExportsWalker {
sb.push("interface ");
sb.push(element.simpleName);
sb.push(" {\n");
for (let member of members.values()) {
this.visitElement(member);
}
for (let member of members.values()) this.visitElement(member);
indent(sb, --this.indentLevel);
sb.push("}\n");
}
@ -234,6 +233,10 @@ export class IDLBuilder extends ExportsWalker {
this.visitClass(element);
}
visitField(element: Field): void {
// TODO
}
visitNamespace(element: Namespace): void {
if (this.seen.has(element)) return;
this.seen.add(element);
@ -244,9 +247,7 @@ export class IDLBuilder extends ExportsWalker {
sb.push(" {\n");
var members = element.members;
if (members) {
for (let member of members.values()) {
this.visitElement(member);
}
for (let member of members.values()) this.visitElement(member);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
@ -311,8 +312,12 @@ export class TSDBuilder extends ExportsWalker {
var sb = this.sb;
var isConst = element.is(CommonFlags.INLINED);
indent(sb, this.indentLevel);
if (isConst) {
sb.push("const ");
if (element.is(CommonFlags.STATIC)) {
if (isConst) sb.push("static readonly ");
else assert(false);
} else {
if (isConst) sb.push("const ");
else sb.push("var ");
}
sb.push(element.simpleName);
sb.push(": ");
@ -345,9 +350,7 @@ export class TSDBuilder extends ExportsWalker {
--numMembers;
}
}
if (numMembers) {
this.visitNamespace(element);
}
if (numMembers) this.visitNamespace(element);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
@ -356,11 +359,22 @@ 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;
indent(sb, this.indentLevel);
sb.push("function ");
sb.push(element.simpleName);
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
if (element.is(CommonFlags.STATIC)) sb.push("static ");
if (element.is(CommonFlags.GET)) {
sb.push("get ");
sb.push(element.prototype.declaration.name.text); // 'get:funcName' internally
} else if (element.is(CommonFlags.SET)) {
sb.push("set ");
sb.push(element.prototype.declaration.name.text);
} else {
if (!element.is(CommonFlags.INSTANCE)) sb.push("function ");
sb.push(element.simpleName);
}
sb.push("(");
var parameters = signature.parameterTypes;
var numParameters = parameters.length;
@ -372,8 +386,12 @@ export class TSDBuilder extends ExportsWalker {
sb.push(": ");
sb.push(this.typeToString(parameters[i]));
}
sb.push("): ");
sb.push(this.typeToString(signature.returnType));
if (element.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.SET)) {
sb.push(")");
} else {
sb.push("): ");
sb.push(this.typeToString(signature.returnType));
}
sb.push(";\n");
this.visitNamespace(element);
}
@ -394,18 +412,22 @@ export class TSDBuilder extends ExportsWalker {
}
sb.push(element.simpleName);
var base = element.base;
if (base) {
if (base && base.is(CommonFlags.COMPILED | CommonFlags.MODULE_EXPORT)) {
sb.push(" extends ");
sb.push(base.simpleName); // TODO: fqn
}
sb.push(" {\n");
var members = element.prototype.members; // static
if (members) {
// TODO
for (let member of members.values()) {
this.visitElement(member);
}
}
var ctor = element.constructorInstance;
if (ctor) this.visitFunction(ctor);
members = element.members; // instance
if (members) {
// TODO
for (let member of members.values()) this.visitElement(member);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
@ -415,6 +437,40 @@ export class TSDBuilder extends ExportsWalker {
this.visitClass(element);
}
visitField(element: Field): void {
if (element.is(CommonFlags.PRIVATE)) return;
var sb = this.sb;
indent(sb, this.indentLevel);
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
if (element.is(CommonFlags.STATIC)) sb.push("static ");
if (element.is(CommonFlags.READONLY)) sb.push("readonly ");
sb.push(element.simpleName);
sb.push(": ");
sb.push(this.typeToString(element.type));
sb.push(";\n");
/* TBD: the compiler implicitly generates setters if the class is exported
indent(sb, this.indentLevel);
sb.push("get ");
sb.push(element.simpleName);
sb.push("(): ");
sb.push(this.typeToString(element.type));
sb.push(";\n");
if (!element.is(CommonFlags.READONLY)) {
indent(sb, this.indentLevel);
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
if (element.is(CommonFlags.STATIC)) sb.push("static ");
if (element.is(CommonFlags.READONLY)) sb.push("readonly ");
sb.push("set ");
sb.push(element.simpleName);
sb.push("(");
sb.push(element.simpleName);
sb.push(": ");
sb.push(this.typeToString(element.type));
sb.push(");\n");
}
*/
}
visitNamespace(element: Element): void {
var members = element.members;
if (members && members.size) {
@ -423,9 +479,7 @@ export class TSDBuilder extends ExportsWalker {
sb.push("namespace ");
sb.push(element.simpleName);
sb.push(" {\n");
for (let member of members.values()) {
this.visitElement(member);
}
for (let member of members.values()) this.visitElement(member);
indent(sb, --this.indentLevel);
sb.push("}\n");
}
@ -477,3 +531,33 @@ 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. */
function hasCompiledMember(element: Element): bool {
var members = element.members;
if (members) {
for (let member of members.values()) {
switch (member.kind) {
case ElementKind.FUNCTION_PROTOTYPE: {
for (let instance of (<FunctionPrototype>member).instances.values()) {
if (instance.is(CommonFlags.COMPILED)) return true;
}
break;
}
case ElementKind.CLASS_PROTOTYPE: {
for (let instance of (<ClassPrototype>member).instances.values()) {
if (instance.is(CommonFlags.COMPILED)) return true;
}
break;
}
default: {
if (member.is(CommonFlags.COMPILED) || hasCompiledMember(member)) return true;
break;
}
}
}
}
return false;
}

View File

@ -28,9 +28,8 @@ import {
CommonTypeNode,
TypeNode,
TypeParameterNode,
// ParameterNode,
// ParameterKind,
// SignatureNode,
ParameterKind,
SignatureNode,
DecoratorNode,
DecoratorKind,
@ -59,12 +58,10 @@ import {
MethodDeclaration,
NamespaceDeclaration,
TypeDeclaration,
VariableDeclaration,
VariableLikeDeclarationStatement,
VariableStatement,
ParameterKind,
SignatureNode,
VariableDeclaration,
stringToDecoratorKind
} from "./ast";
@ -533,7 +530,7 @@ export class Program extends DiagnosticEmitter {
)
: DecoratorFlags.NONE
);
prototype.namespace = namespace;
prototype.parent = namespace;
this.elementsLookup.set(internalName, prototype);
var implementsTypes = declaration.implementsTypes;
@ -573,10 +570,8 @@ export class Program extends DiagnosticEmitter {
namespace.members = new Map();
}
namespace.members.set(simpleName, prototype);
if (namespace.is(CommonFlags.MODULE_EXPORT)) {
if (prototype.is(CommonFlags.EXPORT)) {
prototype.set(CommonFlags.MODULE_EXPORT);
}
if (namespace.is(CommonFlags.MODULE_EXPORT) && prototype.is(CommonFlags.EXPORT)) {
prototype.set(CommonFlags.MODULE_EXPORT);
}
// otherwise add to file-level exports if exported
@ -662,8 +657,12 @@ export class Program extends DiagnosticEmitter {
Type.void, // resolved later on
declaration
);
staticField.parent = classPrototype;
classPrototype.members.set(name, staticField);
this.elementsLookup.set(internalName, staticField);
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
staticField.set(CommonFlags.MODULE_EXPORT);
}
// instance fields are remembered until resolved
} else {
@ -685,6 +684,7 @@ export class Program extends DiagnosticEmitter {
declaration
);
classPrototype.instanceMembers.set(name, instanceField);
// TBD: no need to mark as MODULE_EXPORT
}
}
@ -958,6 +958,7 @@ export class Program extends DiagnosticEmitter {
this.elementsLookup.set(internalPropertyName, propertyElement);
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
propertyElement.set(CommonFlags.MODULE_EXPORT);
instancePrototype.set(CommonFlags.MODULE_EXPORT);
}
}
}
@ -976,7 +977,7 @@ export class Program extends DiagnosticEmitter {
}
var simpleName = declaration.name.text;
var element = new Enum(this, simpleName, internalName, declaration);
element.namespace = namespace;
element.parent = namespace;
this.elementsLookup.set(internalName, element);
if (namespace) {
@ -992,7 +993,7 @@ export class Program extends DiagnosticEmitter {
namespace.members = new Map();
}
namespace.members.set(simpleName, element);
if (namespace.is(CommonFlags.MODULE_EXPORT)) {
if (namespace.is(CommonFlags.MODULE_EXPORT) && element.is(CommonFlags.EXPORT)) {
element.set(CommonFlags.MODULE_EXPORT);
}
} else if (element.is(CommonFlags.EXPORT)) { // no namespace
@ -1031,7 +1032,6 @@ export class Program extends DiagnosticEmitter {
): void {
var name = declaration.name.text;
var internalName = declaration.fileLevelInternalName;
var isModuleExport = enm.is(CommonFlags.MODULE_EXPORT);
if (enm.members) {
if (enm.members.has(name)) {
this.error(
@ -1045,7 +1045,7 @@ export class Program extends DiagnosticEmitter {
}
var value = new EnumValue(enm, this, name, internalName, declaration);
enm.members.set(name, value);
if (isModuleExport) {
if (enm.is(CommonFlags.MODULE_EXPORT)) {
value.set(CommonFlags.MODULE_EXPORT);
}
}
@ -1212,7 +1212,7 @@ export class Program extends DiagnosticEmitter {
)
: DecoratorFlags.NONE
);
prototype.namespace = namespace;
prototype.parent = namespace;
this.elementsLookup.set(internalName, prototype);
if (namespace) {
@ -1229,6 +1229,7 @@ export class Program extends DiagnosticEmitter {
}
namespace.members.set(simpleName, prototype);
if (namespace.is(CommonFlags.MODULE_EXPORT) && prototype.is(CommonFlags.EXPORT)) {
prototype.parent = namespace;
prototype.set(CommonFlags.MODULE_EXPORT);
}
} else if (prototype.is(CommonFlags.EXPORT)) { // no namespace
@ -1358,7 +1359,7 @@ export class Program extends DiagnosticEmitter {
? this.filterDecorators(decorators, DecoratorFlags.GLOBAL)
: DecoratorFlags.NONE
);
prototype.namespace = namespace;
prototype.parent = namespace;
this.elementsLookup.set(internalName, prototype);
if (namespace) {
@ -1436,7 +1437,7 @@ export class Program extends DiagnosticEmitter {
var namespace = this.elementsLookup.get(internalName);
if (!namespace) {
namespace = new Namespace(this, simpleName, internalName, declaration);
namespace.namespace = parentNamespace;
namespace.parent = parentNamespace;
this.elementsLookup.set(internalName, namespace);
this.checkGlobalOptions(namespace, declaration);
}
@ -1563,7 +1564,7 @@ export class Program extends DiagnosticEmitter {
Type.void, // resolved later on
declaration
);
global.namespace = namespace;
global.parent = namespace;
this.elementsLookup.set(internalName, global);
if (namespace) {
@ -1836,14 +1837,14 @@ export class Program extends DiagnosticEmitter {
// }
// search contextual parent namespaces if applicable
if (namespace = contextualFunction.prototype.namespace) {
if (namespace = contextualFunction.prototype.parent) {
do {
if (element = this.elementsLookup.get(namespace.internalName + STATIC_DELIMITER + name)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
return element; // LOCAL
}
} while (namespace = namespace.namespace);
} while (namespace = namespace.parent);
}
}
@ -2061,7 +2062,7 @@ export class Program extends DiagnosticEmitter {
return explicitLocal;
}
}
let parent = contextualFunction.memberOf;
let parent = contextualFunction.parent;
if (parent) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
@ -2082,7 +2083,7 @@ export class Program extends DiagnosticEmitter {
return explicitLocal;
}
}
let parent = contextualFunction.memberOf;
let parent = contextualFunction.parent;
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
this.resolvedThisExpression = null;
this.resolvedElementExpression = null;
@ -2308,8 +2309,8 @@ export abstract class Element {
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE;
/** Namespaced member elements. */
members: Map<string,Element> | null = null;
/** Parent namespace, if applicable. */
namespace: Element | null = null;
/** Parent element, if applicable. */
parent: Element | null = null;
/** Constructs a new element, linking it to its containing {@link Program}. */
protected constructor(program: Program, simpleName: string, internalName: string) {
@ -2378,8 +2379,6 @@ export class EnumValue extends Element {
/** Declaration reference. */
declaration: EnumValueDeclaration;
/** Parent enum. */
enum: Enum;
/** Constant value, if applicable. */
constantValue: i32 = 0;
@ -2391,7 +2390,7 @@ export class EnumValue extends Element {
declaration: EnumValueDeclaration
) {
super(program, simpleName, internalName);
this.enum = enm;
this.parent = enm;
this.declaration = declaration;
}
}
@ -2732,8 +2731,6 @@ export class Function extends Element {
prototype: FunctionPrototype;
/** Function signature. */
signature: Signature;
/** If a member of another namespace-like element, the concrete element it is a member of. */
memberOf: Element | null;
/** Map of locals by name. */
locals: Map<string,Local> = new Map();
/** List of additional non-parameter locals. */
@ -2764,19 +2761,19 @@ export class Function extends Element {
prototype: FunctionPrototype,
internalName: string,
signature: Signature,
memberOf: Element | null = null,
parent: Element | null = null,
contextualTypeArguments: Map<string,Type> | null = null
) {
super(prototype.program, prototype.simpleName, internalName);
this.prototype = prototype;
this.signature = signature;
this.memberOf = memberOf;
this.parent = parent;
this.flags = prototype.flags;
this.decoratorFlags = prototype.decoratorFlags;
this.contextualTypeArguments = contextualTypeArguments;
if (!(prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
let localIndex = 0;
if (memberOf && memberOf.kind == ElementKind.CLASS) {
if (parent && parent.kind == ElementKind.CLASS) {
assert(this.is(CommonFlags.INSTANCE));
this.locals.set(
"this",
@ -2787,7 +2784,7 @@ export class Function extends Element {
assert(signature.thisType)
)
);
let inheritedTypeArguments = (<Class>memberOf).contextualTypeArguments;
let inheritedTypeArguments = (<Class>parent).contextualTypeArguments;
if (inheritedTypeArguments) {
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
for (let [inheritedName, inheritedType] of inheritedTypeArguments) {
@ -3048,12 +3045,14 @@ export class Field extends VariableLikeElement {
prototype: FieldPrototype,
internalName: string,
type: Type,
declaration: FieldDeclaration
declaration: FieldDeclaration,
parent: Class
) {
super(prototype.program, prototype.simpleName, internalName, type, declaration);
this.prototype = prototype;
this.flags = prototype.flags;
this.type = type;
this.parent = parent;
}
}
@ -3219,7 +3218,8 @@ export class ClassPrototype extends Element {
<FieldPrototype>member,
internalName + INSTANCE_DELIMITER + (<FieldPrototype>member).simpleName,
fieldType,
fieldDeclaration
fieldDeclaration,
instance
);
switch (fieldType.byteSize) { // align
case 1: break;

View File

@ -557,19 +557,22 @@ export class Signature {
return thisReturnType == targetReturnType || thisReturnType.isAssignableTo(targetReturnType);
}
/** Converts a signature to a function type string. */
static makeSignatureString(parameterTypes: Type[] | null, returnType: Type, thisType: Type | null = null): string {
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());
}
}
sb.push(returnType.toSignatureString());
return sb.join("");
}
/** Converts this signature to a function type string. */
toSignatureString(): string {
var sb = [];
var thisType = this.thisType;
if (thisType) {
sb.push(thisType.toSignatureString());
}
var parameterTypes = this.parameterTypes;
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
sb.push(parameterTypes[i].toSignatureString());
}
sb.push(this.returnType.toSignatureString());
return sb.join("");
return Signature.makeSignatureString(this.parameterTypes, this.returnType, this.thisType);
}
/** Converts this signature to a string. */