2018-03-17 23:41:48 +01:00
|
|
|
/**
|
2018-03-19 01:12:18 +01:00
|
|
|
* AssemblyScript's intermediate representation describing a program's elements.
|
|
|
|
* @module program
|
|
|
|
*//***/
|
2018-03-17 23:41:48 +01:00
|
|
|
|
2018-06-24 01:04:24 +02:00
|
|
|
import {
|
|
|
|
CommonFlags,
|
|
|
|
PATH_DELIMITER,
|
|
|
|
STATIC_DELIMITER,
|
|
|
|
INSTANCE_DELIMITER,
|
|
|
|
GETTER_PREFIX,
|
|
|
|
SETTER_PREFIX,
|
2019-02-21 00:11:22 +01:00
|
|
|
INNER_DELIMITER,
|
|
|
|
LIBRARY_SUBST,
|
|
|
|
INDEX_SUFFIX,
|
|
|
|
CommonSymbols,
|
|
|
|
LibrarySymbols
|
2018-06-24 01:04:24 +02:00
|
|
|
} from "./common";
|
|
|
|
|
2017-12-24 03:19:47 +01:00
|
|
|
import {
|
2018-06-29 00:14:42 +02:00
|
|
|
Options,
|
2018-12-03 17:22:01 +01:00
|
|
|
Feature
|
2017-12-24 03:19:47 +01:00
|
|
|
} from "./compiler";
|
|
|
|
|
|
|
|
import {
|
|
|
|
DiagnosticCode,
|
|
|
|
DiagnosticMessage,
|
|
|
|
DiagnosticEmitter
|
|
|
|
} from "./diagnostics";
|
|
|
|
|
|
|
|
import {
|
|
|
|
Type,
|
2018-05-06 00:00:54 +02:00
|
|
|
TypeKind,
|
|
|
|
TypeFlags,
|
2018-07-13 00:22:22 +02:00
|
|
|
Signature
|
2017-12-24 03:19:47 +01:00
|
|
|
} from "./types";
|
|
|
|
|
|
|
|
import {
|
2017-09-28 13:08:25 +02:00
|
|
|
Node,
|
|
|
|
NodeKind,
|
2017-10-02 12:52:15 +02:00
|
|
|
Source,
|
2019-02-21 00:11:22 +01:00
|
|
|
SourceKind,
|
2017-10-19 18:55:27 +02:00
|
|
|
Range,
|
2018-03-12 14:06:39 +01:00
|
|
|
DecoratorNode,
|
2018-01-06 10:20:38 +01:00
|
|
|
DecoratorKind,
|
2019-02-21 00:11:22 +01:00
|
|
|
SignatureNode,
|
|
|
|
TypeParameterNode,
|
|
|
|
CommonTypeNode,
|
|
|
|
TypeNode,
|
2017-12-24 03:19:47 +01:00
|
|
|
|
2017-10-19 18:55:27 +02:00
|
|
|
Expression,
|
|
|
|
IdentifierExpression,
|
2018-01-06 10:20:38 +01:00
|
|
|
LiteralExpression,
|
|
|
|
LiteralKind,
|
2017-10-19 18:55:27 +02:00
|
|
|
StringLiteralExpression,
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
Statement,
|
2017-09-28 13:08:25 +02:00
|
|
|
ClassDeclaration,
|
|
|
|
DeclarationStatement,
|
|
|
|
EnumDeclaration,
|
|
|
|
EnumValueDeclaration,
|
2017-09-29 17:25:02 +02:00
|
|
|
ExportMember,
|
|
|
|
ExportStatement,
|
2017-09-28 13:08:25 +02:00
|
|
|
FieldDeclaration,
|
|
|
|
FunctionDeclaration,
|
|
|
|
ImportDeclaration,
|
|
|
|
ImportStatement,
|
|
|
|
InterfaceDeclaration,
|
|
|
|
MethodDeclaration,
|
|
|
|
NamespaceDeclaration,
|
2017-12-19 17:49:15 +01:00
|
|
|
TypeDeclaration,
|
2018-04-15 00:34:19 +02:00
|
|
|
VariableDeclaration,
|
2017-10-19 18:55:27 +02:00
|
|
|
VariableLikeDeclarationStatement,
|
2017-10-11 17:03:22 +02:00
|
|
|
VariableStatement,
|
|
|
|
|
2018-07-19 16:38:09 +02:00
|
|
|
decoratorNameToKind,
|
|
|
|
findDecorator
|
2017-09-28 13:08:25 +02:00
|
|
|
} from "./ast";
|
2017-12-24 03:19:47 +01:00
|
|
|
|
|
|
|
import {
|
2018-02-02 03:07:54 +01:00
|
|
|
Module,
|
2019-02-21 00:11:22 +01:00
|
|
|
FunctionRef
|
2017-12-24 03:19:47 +01:00
|
|
|
} from "./module";
|
2018-06-14 15:57:04 +02:00
|
|
|
|
|
|
|
import {
|
2019-02-21 00:11:22 +01:00
|
|
|
CharCode
|
2018-06-14 15:57:04 +02:00
|
|
|
} from "./util";
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2018-07-13 00:22:22 +02:00
|
|
|
import {
|
|
|
|
Resolver
|
|
|
|
} from "./resolver";
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
import {
|
|
|
|
Flow
|
|
|
|
} from "./flow";
|
|
|
|
|
|
|
|
/** Represents a yet unresolved `import`. */
|
2018-06-14 15:57:04 +02:00
|
|
|
class QueuedImport {
|
2019-02-21 00:11:22 +01:00
|
|
|
constructor(
|
|
|
|
/** File being imported into. */
|
|
|
|
public localFile: File,
|
|
|
|
/** Identifier within the local file. */
|
|
|
|
public localIdentifier: IdentifierExpression,
|
|
|
|
/** Identifier within the other file. Is an `import *` if not set. */
|
|
|
|
public foreignIdentifier: IdentifierExpression | null,
|
|
|
|
/** Path to the other file. */
|
|
|
|
public foreignPath: string,
|
|
|
|
/** Alternative path to the other file. */
|
|
|
|
public foreignPathAlt: string
|
|
|
|
) {}
|
2018-06-14 15:57:04 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Represents a yet unresolved `export`. */
|
2017-09-29 17:25:02 +02:00
|
|
|
class QueuedExport {
|
2019-02-21 00:11:22 +01:00
|
|
|
constructor(
|
|
|
|
/** Identifier within the local file. */
|
|
|
|
public localIdentifier: IdentifierExpression,
|
|
|
|
/** Identifier within the other file. */
|
|
|
|
public foreignIdentifier: IdentifierExpression,
|
|
|
|
/** Path to the other file if a re-export. */
|
|
|
|
public foreignPath: string | null,
|
|
|
|
/** Alternative path to the other file if a re-export. */
|
|
|
|
public foreignPathAlt: string | null
|
|
|
|
) {}
|
2018-03-12 17:44:09 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Represents a yet unresolved `export *`. */
|
|
|
|
class QueuedExportStar {
|
|
|
|
// stored in a map with localFile as the key
|
|
|
|
constructor(
|
|
|
|
/** Path to the other file. */
|
|
|
|
public foreignPath: string,
|
|
|
|
/** Alternative path to the other file. */
|
|
|
|
public foreignPathAlt: string,
|
|
|
|
/** Reference to the path literal for reporting. */
|
|
|
|
public pathLiteral: StringLiteralExpression
|
|
|
|
) {}
|
2018-07-18 23:49:32 +02:00
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Represents the kind of an operator overload. */
|
|
|
|
export enum OperatorKind {
|
|
|
|
INVALID,
|
2018-06-01 14:17:27 +02:00
|
|
|
|
|
|
|
// indexed access
|
|
|
|
INDEXED_GET, // a[]
|
|
|
|
INDEXED_SET, // a[]=b
|
|
|
|
UNCHECKED_INDEXED_GET, // unchecked(a[])
|
|
|
|
UNCHECKED_INDEXED_SET, // unchecked(a[]=b)
|
|
|
|
|
|
|
|
// binary
|
|
|
|
ADD, // a + b
|
|
|
|
SUB, // a - b
|
|
|
|
MUL, // a * b
|
|
|
|
DIV, // a / b
|
|
|
|
REM, // a % b
|
|
|
|
POW, // a ** b
|
|
|
|
BITWISE_AND, // a & b
|
|
|
|
BITWISE_OR, // a | b
|
|
|
|
BITWISE_XOR, // a ^ b
|
|
|
|
BITWISE_SHL, // a << b
|
|
|
|
BITWISE_SHR, // a >> b
|
|
|
|
BITWISE_SHR_U, // a >>> b
|
|
|
|
EQ, // a == b
|
|
|
|
NE, // a != b
|
|
|
|
GT, // a > b
|
|
|
|
GE, // a >= b
|
|
|
|
LT, // a < b
|
|
|
|
LE, // a <= b
|
|
|
|
|
|
|
|
// unary prefix
|
|
|
|
PLUS, // +a
|
|
|
|
MINUS, // -a
|
|
|
|
NOT, // !a
|
|
|
|
BITWISE_NOT, // ~a
|
|
|
|
PREFIX_INC, // ++a
|
|
|
|
PREFIX_DEC, // --a
|
|
|
|
|
|
|
|
// unary postfix
|
|
|
|
POSTFIX_INC, // a++
|
|
|
|
POSTFIX_DEC // a--
|
|
|
|
|
|
|
|
// not overridable:
|
|
|
|
// IDENTITY // a === b
|
|
|
|
// LOGICAL_AND // a && b
|
|
|
|
// LOGICAL_OR // a || b
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
|
|
|
|
2018-06-01 14:17:27 +02:00
|
|
|
/** Returns the operator kind represented by the specified decorator and string argument. */
|
|
|
|
function operatorKindFromDecorator(decoratorKind: DecoratorKind, arg: string): OperatorKind {
|
|
|
|
assert(arg.length);
|
2018-06-05 23:09:08 +02:00
|
|
|
switch (decoratorKind) {
|
|
|
|
case DecoratorKind.OPERATOR:
|
|
|
|
case DecoratorKind.OPERATOR_BINARY: {
|
|
|
|
switch (arg.charCodeAt(0)) {
|
|
|
|
case CharCode.OPENBRACKET: {
|
|
|
|
if (arg == "[]") return OperatorKind.INDEXED_GET;
|
|
|
|
if (arg == "[]=") return OperatorKind.INDEXED_SET;
|
|
|
|
break;
|
2018-06-04 18:23:09 +03:00
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.OPENBRACE: {
|
|
|
|
if (arg == "{}") return OperatorKind.UNCHECKED_INDEXED_GET;
|
|
|
|
if (arg == "{}=") return OperatorKind.UNCHECKED_INDEXED_SET;
|
2018-06-04 18:23:09 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.PLUS: {
|
|
|
|
if (arg == "+") return OperatorKind.ADD;
|
2018-06-04 18:23:09 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.MINUS: {
|
|
|
|
if (arg == "-") return OperatorKind.SUB;
|
|
|
|
break;
|
2018-06-04 18:23:09 +03:00
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.ASTERISK: {
|
|
|
|
if (arg == "*") return OperatorKind.MUL;
|
|
|
|
if (arg == "**") return OperatorKind.POW;
|
2018-06-04 18:23:09 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.SLASH: {
|
|
|
|
if (arg == "/") return OperatorKind.DIV;
|
2018-06-04 18:23:09 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case CharCode.PERCENT: {
|
|
|
|
if (arg == "%") return OperatorKind.REM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.AMPERSAND: {
|
|
|
|
if (arg == "&") return OperatorKind.BITWISE_AND;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.BAR: {
|
|
|
|
if (arg == "|") return OperatorKind.BITWISE_OR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.CARET: {
|
|
|
|
if (arg == "^") return OperatorKind.BITWISE_XOR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.EQUALS: {
|
|
|
|
if (arg == "==") return OperatorKind.EQ;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.EXCLAMATION: {
|
|
|
|
if (arg == "!=") return OperatorKind.NE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.GREATERTHAN: {
|
|
|
|
if (arg == ">") return OperatorKind.GT;
|
|
|
|
if (arg == ">=") return OperatorKind.GE;
|
|
|
|
if (arg == ">>") return OperatorKind.BITWISE_SHR;
|
|
|
|
if (arg == ">>>") return OperatorKind.BITWISE_SHR_U;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.LESSTHAN: {
|
|
|
|
if (arg == "<") return OperatorKind.LT;
|
|
|
|
if (arg == "<=") return OperatorKind.LE;
|
|
|
|
if (arg == "<<") return OperatorKind.BITWISE_SHL;
|
2018-06-04 18:23:09 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-05-30 16:22:56 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case DecoratorKind.OPERATOR_PREFIX: {
|
|
|
|
switch (arg.charCodeAt(0)) {
|
|
|
|
case CharCode.PLUS: {
|
|
|
|
if (arg == "+") return OperatorKind.PLUS;
|
|
|
|
if (arg == "++") return OperatorKind.PREFIX_INC;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.MINUS: {
|
|
|
|
if (arg == "-") return OperatorKind.MINUS;
|
|
|
|
if (arg == "--") return OperatorKind.PREFIX_DEC;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.EXCLAMATION: {
|
|
|
|
if (arg == "!") return OperatorKind.NOT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.TILDE: {
|
|
|
|
if (arg == "~") return OperatorKind.BITWISE_NOT;
|
|
|
|
break;
|
|
|
|
}
|
2018-06-04 18:23:09 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-06-05 23:09:08 +02:00
|
|
|
case DecoratorKind.OPERATOR_POSTFIX: {
|
|
|
|
switch (arg.charCodeAt(0)) {
|
|
|
|
case CharCode.PLUS: {
|
|
|
|
if (arg == "++") return OperatorKind.POSTFIX_INC;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CharCode.MINUS: {
|
|
|
|
if (arg == "--") return OperatorKind.POSTFIX_DEC;
|
|
|
|
break;
|
|
|
|
}
|
2018-05-30 16:22:56 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
|
|
|
return OperatorKind.INVALID;
|
|
|
|
}
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Represents an AssemblyScript program. */
|
2017-09-28 13:08:25 +02:00
|
|
|
export class Program extends DiagnosticEmitter {
|
|
|
|
|
2018-07-13 00:22:22 +02:00
|
|
|
/** Resolver instance. */
|
|
|
|
resolver: Resolver;
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Array of sources. */
|
|
|
|
sources: Source[] = [];
|
2018-07-13 00:22:22 +02:00
|
|
|
/** Diagnostic offset used where successively obtaining the next diagnostic. */
|
2017-09-28 13:08:25 +02:00
|
|
|
diagnosticsOffset: i32 = 0;
|
2018-01-21 17:52:44 +01:00
|
|
|
/** Compiler options. */
|
|
|
|
options: Options;
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Special native code source. */
|
|
|
|
nativeSource: Source;
|
|
|
|
/** Special native code file. */
|
|
|
|
nativeFile: File;
|
|
|
|
/** Explicitly annotated start function. */
|
|
|
|
explicitStartFunction: FunctionPrototype | null = null;
|
|
|
|
|
|
|
|
// lookup maps
|
|
|
|
|
|
|
|
/** Files by unique internal name. */
|
|
|
|
filesByName: Map<string,File> = new Map();
|
|
|
|
/** Elements by unique internal name in element space. */
|
|
|
|
elementsByName: Map<string,Element> = new Map();
|
|
|
|
/** Elements by declaration. */
|
|
|
|
elementsByDeclaration: Map<DeclarationStatement,DeclaredElement> = new Map();
|
|
|
|
/** Element instances by unique internal name. */
|
|
|
|
instancesByName: Map<string,Element> = new Map();
|
2019-01-09 12:45:29 +01:00
|
|
|
/** Classes backing basic types like `i32`. */
|
2019-02-21 00:11:22 +01:00
|
|
|
typeClasses: Map<TypeKind,Class> = new Map();
|
|
|
|
|
|
|
|
// runtime references
|
2018-06-14 15:57:04 +02:00
|
|
|
|
2018-08-04 00:36:59 +02:00
|
|
|
/** ArrayBuffer instance reference. */
|
|
|
|
arrayBufferInstance: Class | null = null;
|
2018-03-31 18:18:55 +02:00
|
|
|
/** Array prototype reference. */
|
|
|
|
arrayPrototype: ClassPrototype | null = null;
|
|
|
|
/** String instance reference. */
|
|
|
|
stringInstance: Class | null = null;
|
2018-07-18 23:49:32 +02:00
|
|
|
/** Abort function reference, if present. */
|
|
|
|
abortInstance: Function | null = null;
|
|
|
|
/** Memory allocation function. */
|
|
|
|
memoryAllocateInstance: Function | null = null;
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// gc integration
|
|
|
|
|
2018-08-02 18:23:02 +02:00
|
|
|
/** Whether a garbage collector is present or not. */
|
|
|
|
hasGC: bool = false;
|
|
|
|
/** Garbage collector allocation function. */
|
|
|
|
gcAllocateInstance: Function | null = null;
|
|
|
|
/** Garbage collector link function called when a managed object is referenced from a parent. */
|
|
|
|
gcLinkInstance: Function | null = null;
|
|
|
|
/** Garbage collector mark function called to on reachable managed objects. */
|
|
|
|
gcMarkInstance: Function | null = null;
|
|
|
|
/** Size of a managed object header. */
|
|
|
|
gcHeaderSize: u32 = 0;
|
2018-08-04 00:36:59 +02:00
|
|
|
/** Offset of the GC hook. */
|
|
|
|
gcHookOffset: u32 = 0;
|
2018-08-02 18:23:02 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new program, optionally inheriting parser diagnostics. */
|
2019-02-21 00:11:22 +01:00
|
|
|
constructor(
|
|
|
|
/** Shared array of diagnostic messages (emitted so far). */
|
|
|
|
diagnostics: DiagnosticMessage[] | null = null
|
|
|
|
) {
|
2017-09-28 13:08:25 +02:00
|
|
|
super(diagnostics);
|
2019-02-21 00:11:22 +01:00
|
|
|
var nativeSource = new Source(LIBRARY_SUBST, "[native code]", SourceKind.LIBRARY);
|
|
|
|
this.nativeSource = nativeSource;
|
|
|
|
var nativeFile = new File(this, nativeSource);
|
|
|
|
this.nativeFile = nativeFile;
|
|
|
|
this.filesByName.set(nativeFile.internalName, nativeFile);
|
2018-07-13 00:22:22 +02:00
|
|
|
this.resolver = new Resolver(this);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Creates a native variable declaration. */
|
|
|
|
makeNativeVariableDeclaration(
|
|
|
|
/** The simple name of the variable */
|
|
|
|
name: string,
|
|
|
|
/** Flags indicating specific traits, e.g. `CONST`. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE
|
|
|
|
): VariableDeclaration {
|
|
|
|
var range = this.nativeSource.range;
|
|
|
|
return Node.createVariableDeclaration(
|
|
|
|
Node.createIdentifierExpression(name, range),
|
|
|
|
null, null, null, flags, range
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Creates a native type declaration. */
|
|
|
|
makeNativeTypeDeclaration(
|
|
|
|
/** The simple name of the type. */
|
|
|
|
name: string,
|
|
|
|
/** Flags indicating specific traits, e.g. `GENERIC`. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE
|
|
|
|
): TypeDeclaration {
|
|
|
|
var range = this.nativeSource.range;
|
|
|
|
var identifier = Node.createIdentifierExpression(name, range);
|
|
|
|
return Node.createTypeDeclaration(
|
|
|
|
identifier,
|
|
|
|
null,
|
|
|
|
Node.createOmittedType(range),
|
|
|
|
null, flags, range
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// a dummy signature for programmatically generated native functions
|
|
|
|
private nativeDummySignature: SignatureNode | null = null;
|
|
|
|
|
|
|
|
/** Creates a native function declaration. */
|
|
|
|
makeNativeFunctionDeclaration(
|
|
|
|
/** The simple name of the function. */
|
|
|
|
name: string,
|
|
|
|
/** Flags indicating specific traits, e.g. `DECLARE`. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE
|
|
|
|
): FunctionDeclaration {
|
|
|
|
var range = this.nativeSource.range;
|
|
|
|
return Node.createFunctionDeclaration(
|
|
|
|
Node.createIdentifierExpression(name, range),
|
|
|
|
null,
|
|
|
|
this.nativeDummySignature || (this.nativeDummySignature = Node.createSignature([],
|
|
|
|
Node.createType( // ^ AST signature doesn't really matter, is overridden anyway
|
|
|
|
Node.createSimpleTypeName(CommonSymbols.void_, range),
|
|
|
|
null, false, range
|
|
|
|
),
|
|
|
|
null, false, range)
|
|
|
|
),
|
|
|
|
null, null, flags, range
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Creates a native namespace declaration. */
|
|
|
|
makeNativeNamespaceDeclaration(
|
|
|
|
/** The simple name of the namespace. */
|
|
|
|
name: string,
|
|
|
|
/** Flags indicating specific traits, e.g. `EXPORT`. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE
|
|
|
|
): NamespaceDeclaration {
|
|
|
|
var range = this.nativeSource.range;
|
|
|
|
return Node.createNamespaceDeclaration(
|
|
|
|
Node.createIdentifierExpression(name, range),
|
|
|
|
[], null, flags, range
|
|
|
|
);
|
2018-04-07 03:27:22 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Creates a native function. */
|
|
|
|
makeNativeFunction(
|
|
|
|
/** The simple name of the function. */
|
|
|
|
name: string,
|
|
|
|
/** Concrete function signature. */
|
|
|
|
signature: Signature,
|
|
|
|
/** Parent element, usually a file, class or namespace. */
|
|
|
|
parent: Element = this.nativeFile,
|
|
|
|
/** Flags indicating specific traits, e.g. `GENERIC`. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE,
|
|
|
|
/** Decorator flags representing built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
|
|
|
): Function {
|
|
|
|
return new Function(
|
|
|
|
name,
|
|
|
|
new FunctionPrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
this.makeNativeFunctionDeclaration(name, flags),
|
|
|
|
decoratorFlags
|
|
|
|
),
|
|
|
|
signature
|
2018-04-07 03:27:22 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Gets the (possibly merged) program element linked to the specified declaration. */
|
|
|
|
getElementByDeclaration(declaration: DeclarationStatement): DeclaredElement {
|
|
|
|
var elementsByDeclaration = this.elementsByDeclaration;
|
|
|
|
assert(elementsByDeclaration.has(declaration));
|
|
|
|
return elementsByDeclaration.get(declaration)!;
|
|
|
|
}
|
|
|
|
|
2017-10-19 18:55:27 +02:00
|
|
|
/** Initializes the program and its elements prior to compilation. */
|
2018-01-21 17:52:44 +01:00
|
|
|
initialize(options: Options): void {
|
|
|
|
this.options = options;
|
2018-06-29 00:14:42 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// register native types
|
|
|
|
this.registerNativeType(CommonSymbols.i8, Type.i8);
|
|
|
|
this.registerNativeType(CommonSymbols.i16, Type.i16);
|
|
|
|
this.registerNativeType(CommonSymbols.i32, Type.i32);
|
|
|
|
this.registerNativeType(CommonSymbols.i64, Type.i64);
|
|
|
|
this.registerNativeType(CommonSymbols.isize, options.isizeType);
|
|
|
|
this.registerNativeType(CommonSymbols.u8, Type.u8);
|
|
|
|
this.registerNativeType(CommonSymbols.u16, Type.u16);
|
|
|
|
this.registerNativeType(CommonSymbols.u32, Type.u32);
|
|
|
|
this.registerNativeType(CommonSymbols.u64, Type.u64);
|
|
|
|
this.registerNativeType(CommonSymbols.usize, options.usizeType);
|
|
|
|
this.registerNativeType(CommonSymbols.bool, Type.bool);
|
|
|
|
this.registerNativeType(CommonSymbols.f32, Type.f32);
|
|
|
|
this.registerNativeType(CommonSymbols.f64, Type.f64);
|
|
|
|
this.registerNativeType(CommonSymbols.void_, Type.void);
|
|
|
|
this.registerNativeType(CommonSymbols.number, Type.f64); // alias
|
|
|
|
this.registerNativeType(CommonSymbols.boolean, Type.bool); // alias
|
|
|
|
this.nativeFile.add(CommonSymbols.native, new TypeDefinition(
|
|
|
|
CommonSymbols.native,
|
|
|
|
this.nativeFile,
|
|
|
|
this.makeNativeTypeDeclaration(CommonSymbols.native, CommonFlags.EXPORT | CommonFlags.GENERIC),
|
|
|
|
DecoratorFlags.BUILTIN
|
|
|
|
));
|
|
|
|
if (options.hasFeature(Feature.SIMD)) this.registerNativeType(CommonSymbols.v128, Type.v128);
|
|
|
|
|
|
|
|
// register compiler hints
|
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_TARGET, Type.i32,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.isWasm64 ? 2 : 1));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_NO_ASSERT, Type.bool,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.noAssert ? 1 : 0, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_MEMORY_BASE, Type.i32,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.memoryBase, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_OPTIMIZE_LEVEL, Type.i32,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.optimizeLevelHint, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_SHRINK_LEVEL, Type.i32,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.shrinkLevelHint, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_FEATURE_MUTABLE_GLOBAL, Type.bool,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.hasFeature(Feature.MUTABLE_GLOBAL) ? 1 : 0, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_FEATURE_SIGN_EXTENSION, Type.bool,
|
2018-06-29 00:14:42 +02:00
|
|
|
i64_new(options.hasFeature(Feature.SIGN_EXTENSION) ? 1 : 0, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_FEATURE_BULK_MEMORY, Type.bool,
|
2019-02-07 15:26:26 +01:00
|
|
|
i64_new(options.hasFeature(Feature.BULK_MEMORY) ? 1 : 0, 0));
|
2019-02-21 00:11:22 +01:00
|
|
|
this.registerConstantInteger(LibrarySymbols.ASC_FEATURE_SIMD, Type.bool,
|
2019-02-07 15:26:26 +01:00
|
|
|
i64_new(options.hasFeature(Feature.SIMD) ? 1 : 0, 0));
|
2018-06-29 00:14:42 +02:00
|
|
|
|
|
|
|
// remember deferred elements
|
2017-12-28 04:09:40 +01:00
|
|
|
var queuedImports = new Array<QueuedImport>();
|
2019-02-21 00:11:22 +01:00
|
|
|
var queuedExports = new Map<File,Map<string,QueuedExport>>();
|
|
|
|
var queuedExportsStar = new Map<File,QueuedExportStar[]>();
|
2018-04-11 23:35:19 +02:00
|
|
|
var queuedExtends = new Array<ClassPrototype>();
|
|
|
|
var queuedImplements = new Array<ClassPrototype>();
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// initialize relevant declaration-like statements of the entire program
|
2018-03-12 14:06:39 +01:00
|
|
|
for (let i = 0, k = this.sources.length; i < k; ++i) {
|
|
|
|
let source = this.sources[i];
|
2019-02-21 00:11:22 +01:00
|
|
|
let file = new File(this, source);
|
|
|
|
this.filesByName.set(file.internalName, file);
|
2018-03-12 14:06:39 +01:00
|
|
|
let statements = source.statements;
|
|
|
|
for (let j = 0, l = statements.length; j < l; ++j) {
|
|
|
|
let statement = statements[j];
|
2017-09-28 13:08:25 +02:00
|
|
|
switch (statement.kind) {
|
2019-02-21 00:11:22 +01:00
|
|
|
case NodeKind.EXPORT: {
|
|
|
|
this.initializeExports(<ExportStatement>statement, file, queuedExports, queuedExportsStar);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case NodeKind.IMPORT: {
|
|
|
|
this.initializeImports(<ImportStatement>statement, file, queuedImports, queuedExports);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case NodeKind.VARIABLE: {
|
|
|
|
this.initializeVariables(<VariableStatement>statement, file);
|
2017-09-29 17:25:02 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case NodeKind.CLASSDECLARATION: {
|
|
|
|
this.initializeClass(<ClassDeclaration>statement, file, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case NodeKind.ENUMDECLARATION: {
|
|
|
|
this.initializeEnum(<EnumDeclaration>statement, file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NodeKind.FUNCTIONDECLARATION: {
|
|
|
|
this.initializeFunction(<FunctionDeclaration>statement, file);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.INTERFACEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeInterface(<InterfaceDeclaration>statement, file);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.NAMESPACEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeNamespace(<NamespaceDeclaration>statement, file, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.TYPEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeTypeDefinition(<TypeDeclaration>statement, file);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// queued exports * should be linkable now that all files have been processed
|
|
|
|
for (let [file, exportsStar] of queuedExportsStar) {
|
|
|
|
for (let i = 0, k = exportsStar.length; i < k; ++i) {
|
|
|
|
let exportStar = exportsStar[i];
|
|
|
|
let foreignFile = this.lookupForeignFile(exportStar.foreignPath, exportStar.foreignPathAlt);
|
|
|
|
if (!foreignFile) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.File_0_not_found,
|
|
|
|
exportStar.pathLiteral.range, exportStar.pathLiteral.value
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
file.ensureExportStar(foreignFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
// queued imports should be resolvable now through traversing exports and queued exports
|
2019-02-21 00:11:22 +01:00
|
|
|
for (let i = 0, k = queuedImports.length; i < k; ++i) {
|
2018-03-12 14:06:39 +01:00
|
|
|
let queuedImport = queuedImports[i];
|
2019-02-21 00:11:22 +01:00
|
|
|
let foreignIdentifier = queuedImport.foreignIdentifier;
|
|
|
|
if (foreignIdentifier) { // i.e. import { foo [as bar] } from "./baz"
|
|
|
|
let element = this.lookupForeign(
|
|
|
|
foreignIdentifier.text,
|
|
|
|
queuedImport.foreignPath,
|
|
|
|
queuedImport.foreignPathAlt,
|
|
|
|
queuedExports
|
|
|
|
);
|
2018-06-12 00:45:19 +02:00
|
|
|
if (element) {
|
2019-02-21 00:11:22 +01:00
|
|
|
queuedImport.localFile.add(
|
|
|
|
queuedImport.localIdentifier.text,
|
|
|
|
element,
|
|
|
|
true // isImport
|
|
|
|
);
|
2018-02-16 11:55:13 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Module_0_has_no_exported_member_1,
|
|
|
|
foreignIdentifier.range,
|
|
|
|
queuedImport.foreignPath,
|
|
|
|
foreignIdentifier.text
|
|
|
|
);
|
2018-06-12 00:45:19 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
} else { // i.e. import * as bar from "./bar"
|
|
|
|
let foreignFile = this.lookupForeignFile(queuedImport.foreignPath, queuedImport.foreignPathAlt);
|
|
|
|
if (foreignFile) {
|
|
|
|
let localFile = queuedImport.localFile;
|
|
|
|
let localName = queuedImport.localIdentifier.text;
|
|
|
|
localFile.add(
|
|
|
|
localName,
|
|
|
|
foreignFile.asImportedNamespace(
|
|
|
|
localName,
|
|
|
|
localFile
|
|
|
|
),
|
|
|
|
true // isImport
|
|
|
|
);
|
2018-06-12 00:45:19 +02:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
assert(false); // already reported by the parser not finding the file
|
2018-02-16 11:55:13 +01:00
|
|
|
}
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
// queued exports should be resolvable now that imports are finalized
|
2019-02-21 00:11:22 +01:00
|
|
|
for (let [file, exports] of queuedExports) {
|
|
|
|
for (let [exportName, queuedExport] of exports) {
|
|
|
|
let localName = queuedExport.localIdentifier.text;
|
|
|
|
let foreignPath = queuedExport.foreignPath;
|
|
|
|
if (foreignPath) { // i.e. export { foo [as bar] } from "./baz"
|
|
|
|
let element = this.lookupForeign(
|
|
|
|
localName,
|
|
|
|
foreignPath,
|
|
|
|
assert(queuedExport.foreignPathAlt), // must be set if foreignPath is
|
|
|
|
queuedExports
|
|
|
|
);
|
|
|
|
if (element) {
|
|
|
|
file.ensureExport(exportName, element);
|
|
|
|
} else {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Module_0_has_no_exported_member_1,
|
2019-02-21 00:11:22 +01:00
|
|
|
queuedExport.localIdentifier.range,
|
|
|
|
foreignPath, localName
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
} else { // i.e. export { foo [as bar] }
|
|
|
|
let element = file.lookupInSelf(localName);
|
|
|
|
if (element) {
|
|
|
|
file.ensureExport(exportName, element);
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
let globalElement = this.lookupGlobal(localName);
|
|
|
|
if (globalElement && globalElement instanceof DeclaredElement) { // export { memory }
|
|
|
|
file.ensureExport(exportName, <DeclaredElement>globalElement);
|
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Module_0_has_no_exported_member_1,
|
|
|
|
queuedExport.foreignIdentifier.range,
|
|
|
|
file.internalName, queuedExport.foreignIdentifier.text
|
|
|
|
);
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
2018-01-07 15:07:46 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// register classes backing basic types
|
|
|
|
this.registerNativeTypeClass(TypeKind.I8, LibrarySymbols.I8);
|
|
|
|
this.registerNativeTypeClass(TypeKind.I16, LibrarySymbols.I16);
|
|
|
|
this.registerNativeTypeClass(TypeKind.I32, LibrarySymbols.I32);
|
|
|
|
this.registerNativeTypeClass(TypeKind.I64, LibrarySymbols.I64);
|
|
|
|
this.registerNativeTypeClass(TypeKind.ISIZE, LibrarySymbols.Isize);
|
|
|
|
this.registerNativeTypeClass(TypeKind.U8, LibrarySymbols.U8);
|
|
|
|
this.registerNativeTypeClass(TypeKind.U16, LibrarySymbols.U16);
|
|
|
|
this.registerNativeTypeClass(TypeKind.U32, LibrarySymbols.U32);
|
|
|
|
this.registerNativeTypeClass(TypeKind.U64, LibrarySymbols.U64);
|
|
|
|
this.registerNativeTypeClass(TypeKind.USIZE, LibrarySymbols.Usize);
|
|
|
|
this.registerNativeTypeClass(TypeKind.BOOL, LibrarySymbols.Bool);
|
|
|
|
this.registerNativeTypeClass(TypeKind.F32, LibrarySymbols.F32);
|
|
|
|
this.registerNativeTypeClass(TypeKind.F64, LibrarySymbols.F64);
|
|
|
|
if (options.hasFeature(Feature.SIMD)) this.registerNativeTypeClass(TypeKind.V128, LibrarySymbols.V128);
|
|
|
|
|
2018-01-07 15:07:46 +01:00
|
|
|
// resolve base prototypes of derived classes
|
2018-07-13 00:22:22 +02:00
|
|
|
var resolver = this.resolver;
|
2018-04-11 23:35:19 +02:00
|
|
|
for (let i = 0, k = queuedExtends.length; i < k; ++i) {
|
2019-02-21 00:11:22 +01:00
|
|
|
let thisPrototype = queuedExtends[i];
|
|
|
|
let extendsNode = assert(thisPrototype.extendsNode); // must be present if in queuedExtends
|
|
|
|
let baseElement = resolver.resolveTypeName(extendsNode.name, thisPrototype.parent); // reports
|
2018-04-11 23:35:19 +02:00
|
|
|
if (!baseElement) continue;
|
|
|
|
if (baseElement.kind == ElementKind.CLASS_PROTOTYPE) {
|
|
|
|
let basePrototype = <ClassPrototype>baseElement;
|
2019-02-21 00:11:22 +01:00
|
|
|
if (basePrototype.hasDecorator(DecoratorFlags.SEALED)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Class_0_is_sealed_and_cannot_be_extended,
|
|
|
|
extendsNode.range, (<ClassPrototype>baseElement).identifierNode.text
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
basePrototype.hasDecorator(DecoratorFlags.UNMANAGED) !=
|
|
|
|
thisPrototype.hasDecorator(DecoratorFlags.UNMANAGED)
|
|
|
|
) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa,
|
|
|
|
Range.join(thisPrototype.identifierNode.range, extendsNode.range)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
thisPrototype.basePrototype = basePrototype;
|
2018-04-05 02:23:03 +02:00
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.A_class_may_only_extend_another_class,
|
2019-02-21 00:11:22 +01:00
|
|
|
extendsNode.range
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-01-07 15:07:46 +01:00
|
|
|
}
|
|
|
|
}
|
2018-03-26 16:54:25 +02:00
|
|
|
|
|
|
|
// set up global aliases
|
2018-07-18 23:49:32 +02:00
|
|
|
{
|
|
|
|
let globalAliases = options.globalAliases;
|
|
|
|
if (globalAliases) {
|
|
|
|
for (let [alias, name] of globalAliases) {
|
|
|
|
if (!name.length) continue; // explicitly disabled
|
2019-02-21 00:11:22 +01:00
|
|
|
let elementsByName = this.elementsByName;
|
|
|
|
let element = elementsByName.get(name);
|
|
|
|
if (element) {
|
|
|
|
if (elementsByName.has(alias)) throw new Error("duplicate global element: " + name);
|
|
|
|
elementsByName.set(alias, element);
|
|
|
|
}
|
|
|
|
else throw new Error("no such global element: " + name);
|
2018-03-31 18:18:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-27 19:03:39 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// register global library elements
|
2018-06-27 19:03:39 +02:00
|
|
|
{
|
2019-02-21 00:11:22 +01:00
|
|
|
let element: Element | null;
|
|
|
|
if (element = this.lookupGlobal(LibrarySymbols.String)) {
|
|
|
|
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
this.stringInstance = resolver.resolveClass(<ClassPrototype>element, null);
|
2018-06-27 19:03:39 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
if (element = this.lookupGlobal(LibrarySymbols.ArrayBuffer)) {
|
|
|
|
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
this.arrayBufferInstance = resolver.resolveClass(<ClassPrototype>element, null);
|
|
|
|
}
|
|
|
|
if (element = this.lookupGlobal(LibrarySymbols.Array)) {
|
|
|
|
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
this.arrayPrototype = <ClassPrototype>element;
|
|
|
|
}
|
|
|
|
if (element = this.lookupGlobal(LibrarySymbols.abort)) {
|
|
|
|
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
|
|
|
this.abortInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
|
|
|
}
|
|
|
|
if (element = this.lookupGlobal(LibrarySymbols.memory)) {
|
|
|
|
if (element = element.lookupInSelf(LibrarySymbols.allocate)) {
|
2018-07-18 23:49:32 +02:00
|
|
|
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
2019-02-21 00:11:22 +01:00
|
|
|
this.memoryAllocateInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
2018-07-18 23:49:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-02 18:23:02 +02:00
|
|
|
|
|
|
|
// register GC hooks if present
|
2019-02-21 00:11:22 +01:00
|
|
|
// FIXME: think about a better way than globals to model this, maybe a GC namespace that can be
|
|
|
|
// dynamically extended by a concrete implementation but then has `@unsafe` methods that normal
|
|
|
|
// code cannot call without explicitly enabling it with a flag.
|
2018-08-02 18:23:02 +02:00
|
|
|
if (
|
2019-02-21 00:11:22 +01:00
|
|
|
this.elementsByName.has("__gc_allocate") &&
|
|
|
|
this.elementsByName.has("__gc_link") &&
|
|
|
|
this.elementsByName.has("__gc_mark")
|
2018-08-02 18:23:02 +02:00
|
|
|
) {
|
|
|
|
// __gc_allocate(usize, (ref: usize) => void): usize
|
2019-02-21 00:11:22 +01:00
|
|
|
let element = <Element>this.elementsByName.get("__gc_allocate");
|
2018-08-02 18:23:02 +02:00
|
|
|
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
|
|
|
let gcAllocateInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
|
|
|
let signature = gcAllocateInstance.signature;
|
|
|
|
assert(signature.parameterTypes.length == 2);
|
|
|
|
assert(signature.parameterTypes[0] == this.options.usizeType);
|
|
|
|
assert(signature.parameterTypes[1].signatureReference);
|
|
|
|
assert(signature.returnType == this.options.usizeType);
|
|
|
|
|
|
|
|
// __gc_link(usize, usize): void
|
2019-02-21 00:11:22 +01:00
|
|
|
element = <Element>this.elementsByName.get("__gc_link");
|
2018-08-02 18:23:02 +02:00
|
|
|
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
|
|
|
let gcLinkInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
|
|
|
signature = gcLinkInstance.signature;
|
|
|
|
assert(signature.parameterTypes.length == 2);
|
|
|
|
assert(signature.parameterTypes[0] == this.options.usizeType);
|
|
|
|
assert(signature.parameterTypes[1] == this.options.usizeType);
|
|
|
|
assert(signature.returnType == Type.void);
|
|
|
|
|
|
|
|
// __gc_mark(usize): void
|
2019-02-21 00:11:22 +01:00
|
|
|
element = <Element>this.elementsByName.get("__gc_mark");
|
2018-08-02 18:23:02 +02:00
|
|
|
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
|
|
|
let gcMarkInstance = assert(this.resolver.resolveFunction(<FunctionPrototype>element, null));
|
|
|
|
signature = gcMarkInstance.signature;
|
|
|
|
assert(signature.parameterTypes.length == 1);
|
|
|
|
assert(signature.parameterTypes[0] == this.options.usizeType);
|
|
|
|
assert(signature.returnType == Type.void);
|
|
|
|
|
|
|
|
this.gcAllocateInstance = gcAllocateInstance;
|
|
|
|
this.gcLinkInstance = gcLinkInstance;
|
|
|
|
this.gcMarkInstance = gcMarkInstance;
|
2018-08-04 00:36:59 +02:00
|
|
|
let gcHookOffset = 2 * options.usizeType.byteSize; // .next + .prev
|
|
|
|
this.gcHookOffset = gcHookOffset;
|
|
|
|
this.gcHeaderSize = (gcHookOffset + 4 + 7) & ~7; // + .hook index + alignment
|
2018-08-02 18:23:02 +02:00
|
|
|
this.hasGC = true;
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
|
|
|
|
// mark module exports, i.e. to apply proper wrapping behavior on the boundaries
|
|
|
|
for (let file of this.filesByName.values()) {
|
|
|
|
let exports = file.exports;
|
|
|
|
if (!(file.source.isEntry && exports)) continue;
|
|
|
|
for (let element of exports.values()) this.markModuleExport(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Marks an element and its children as a module export. */
|
|
|
|
private markModuleExport(element: Element): void {
|
|
|
|
element.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
switch (element.kind) {
|
|
|
|
case ElementKind.CLASS_PROTOTYPE: {
|
|
|
|
let instanceMembers = (<ClassPrototype>element).instanceMembers;
|
|
|
|
if (instanceMembers) for (let member of instanceMembers.values()) this.markModuleExport(member);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.PROPERTY_PROTOTYPE: {
|
|
|
|
let getterPrototype = (<PropertyPrototype>element).getterPrototype;
|
|
|
|
if (getterPrototype) this.markModuleExport(getterPrototype);
|
|
|
|
let setterPrototype = (<PropertyPrototype>element).setterPrototype;
|
|
|
|
if (setterPrototype) this.markModuleExport(setterPrototype);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.PROPERTY:
|
|
|
|
case ElementKind.FUNCTION:
|
|
|
|
case ElementKind.FIELD:
|
|
|
|
case ElementKind.CLASS: assert(false); // assumes that there are no instances yet
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let members = element.members;
|
|
|
|
if (members) for (let member of members.values()) this.markModuleExport(member);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Registers a native type with the program. */
|
|
|
|
private registerNativeType(name: string, type: Type): void {
|
|
|
|
var element = new TypeDefinition(
|
|
|
|
name,
|
|
|
|
this.nativeFile,
|
|
|
|
this.makeNativeTypeDeclaration(name, CommonFlags.EXPORT),
|
|
|
|
DecoratorFlags.BUILTIN
|
|
|
|
);
|
|
|
|
element.setType(type);
|
|
|
|
this.nativeFile.add(name, element);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Registers the backing class of a native type. */
|
|
|
|
private registerNativeTypeClass(typeKind: TypeKind, className: string): void {
|
|
|
|
assert(!this.typeClasses.has(typeKind));
|
|
|
|
var element = this.lookupGlobal(className);
|
|
|
|
if (element) {
|
2019-01-09 12:45:29 +01:00
|
|
|
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
let classElement = this.resolver.resolveClass(<ClassPrototype>element, null);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (classElement) this.typeClasses.set(typeKind, classElement);
|
2019-01-09 12:45:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Registers a constant integer value within the global scope. */
|
|
|
|
private registerConstantInteger(name: string, type: Type, value: I64): void {
|
|
|
|
assert(type.is(TypeFlags.INTEGER)); // must be an integer type
|
|
|
|
var global = new Global(
|
|
|
|
name,
|
|
|
|
this.nativeFile,
|
|
|
|
DecoratorFlags.NONE,
|
|
|
|
this.makeNativeVariableDeclaration(name, CommonFlags.CONST | CommonFlags.EXPORT)
|
|
|
|
);
|
|
|
|
global.setConstantIntegerValue(value, type);
|
|
|
|
this.nativeFile.add(name, global);
|
2018-06-29 00:14:42 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Registers a constant float value within the global scope. */
|
|
|
|
private registerConstantFloat(name: string, type: Type, value: f64): void {
|
|
|
|
assert(type.is(TypeFlags.FLOAT)); // must be a float type
|
|
|
|
var global = new Global(
|
|
|
|
name,
|
|
|
|
this.nativeFile,
|
|
|
|
DecoratorFlags.NONE,
|
|
|
|
this.makeNativeVariableDeclaration(name, CommonFlags.CONST | CommonFlags.EXPORT)
|
|
|
|
);
|
|
|
|
global.setConstantFloatValue(value, type);
|
|
|
|
this.nativeFile.add(name, global);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Ensures that the given global element exists. Attempts to merge duplicates. */
|
|
|
|
ensureGlobal(name: string, element: DeclaredElement): void {
|
|
|
|
var elementsByName = this.elementsByName;
|
|
|
|
if (elementsByName.has(name)) {
|
|
|
|
let actual = elementsByName.get(name);
|
|
|
|
// NOTE: this is effectively only performed when merging native types with
|
|
|
|
// their respective namespaces in std/builtins, but can also trigger when a
|
|
|
|
// user has multiple global elements of the same name in different files,
|
|
|
|
// which might result in unexpected shared symbols accross files. considering
|
|
|
|
// this a wonky feature for now that we might want to revisit later.
|
|
|
|
if (actual !== element) {
|
|
|
|
let merged = tryMerge(elementsByName.get(name)!, element);
|
|
|
|
if (!merged) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
element.identifierNode.range, name
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
element = merged;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elementsByName.set(name, element);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Looks up the element of the specified name in the global scope. */
|
|
|
|
lookupGlobal(name: string): Element | null {
|
|
|
|
var elements = this.elementsByName;
|
|
|
|
if (elements.has(name)) return elements.get(name);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tries to locate a foreign file given its normalized path. */
|
|
|
|
private lookupForeignFile(
|
|
|
|
/** Normalized path to the other file. */
|
|
|
|
foreignPath: string,
|
|
|
|
/** Alternative normalized path to the other file. */
|
|
|
|
foreignPathAlt: string
|
|
|
|
): File | null {
|
|
|
|
var filesByName = this.filesByName;
|
|
|
|
return filesByName.has(foreignPath)
|
|
|
|
? filesByName.get(foreignPath)!
|
|
|
|
: filesByName.has(foreignPathAlt)
|
|
|
|
? filesByName.get(foreignPathAlt)!
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tries to locate a foreign element by traversing exports and queued exports. */
|
|
|
|
private lookupForeign(
|
|
|
|
/** Identifier within the other file. */
|
|
|
|
foreignName: string,
|
|
|
|
/** Normalized path to the other file. */
|
|
|
|
foreignPath: string,
|
|
|
|
/** Alternative normalized path to the other file. */
|
|
|
|
foreignPathAlt: string,
|
|
|
|
/** So far queued exports. */
|
|
|
|
queuedExports: Map<File,Map<string,QueuedExport>>
|
|
|
|
): DeclaredElement | null {
|
2017-12-02 01:14:15 +01:00
|
|
|
do {
|
2019-02-21 00:11:22 +01:00
|
|
|
let foreignFile = this.lookupForeignFile(foreignPath, foreignPathAlt);
|
|
|
|
if (!foreignFile) return null; // no such file
|
|
|
|
|
|
|
|
// search already resolved exports
|
|
|
|
let element = foreignFile.lookupExport(foreignName);
|
|
|
|
if (element) return element;
|
|
|
|
|
|
|
|
// otherwise traverse queued exports
|
|
|
|
if (queuedExports.has(foreignFile)) {
|
|
|
|
let fileQueuedExports = queuedExports.get(foreignFile)!;
|
|
|
|
if (fileQueuedExports.has(foreignName)) {
|
|
|
|
let queuedExport = fileQueuedExports.get(foreignName)!;
|
|
|
|
if (queuedExport.foreignPath) { // imported from another file
|
|
|
|
foreignName = queuedExport.localIdentifier.text;
|
|
|
|
foreignPath = queuedExport.foreignPath;
|
|
|
|
foreignPathAlt = assert(queuedExport.foreignPathAlt);
|
|
|
|
continue;
|
|
|
|
} else { // local element of this file
|
|
|
|
element = foreignFile.lookupInSelf(queuedExport.localIdentifier.text);
|
|
|
|
if (element) return element;
|
|
|
|
}
|
|
|
|
}
|
2017-12-02 01:14:15 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2017-12-02 01:14:15 +01:00
|
|
|
} while (true);
|
2018-06-14 15:57:04 +02:00
|
|
|
return null;
|
2017-12-02 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Validates that only supported decorators are present. */
|
2018-07-13 00:22:22 +02:00
|
|
|
private checkDecorators(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Decorators present on an element. */
|
|
|
|
decorators: DecoratorNode[] | null,
|
|
|
|
/** Accepted decorator flags. Emits diagnostics if any other decorators are present. */
|
2018-07-13 00:22:22 +02:00
|
|
|
acceptedFlags: DecoratorFlags
|
|
|
|
): DecoratorFlags {
|
2019-02-21 00:11:22 +01:00
|
|
|
var flags = DecoratorFlags.NONE;
|
|
|
|
if (decorators) {
|
|
|
|
for (let i = 0, k = decorators.length; i < k; ++i) {
|
|
|
|
let decorator = decorators[i];
|
|
|
|
let kind = decoratorNameToKind(decorator.name);
|
|
|
|
let flag = decoratorKindToFlag(kind);
|
|
|
|
if (flag) {
|
|
|
|
if (flag == DecoratorFlags.BUILTIN) {
|
|
|
|
if (decorator.range.source.isLibrary) {
|
|
|
|
flags |= flag;
|
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Decorator_0_is_not_valid_here,
|
|
|
|
decorator.range, decorator.name.range.toString()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else if (!(acceptedFlags & flag)) {
|
2018-07-19 02:10:04 +02:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Decorator_0_is_not_valid_here,
|
|
|
|
decorator.range, decorator.name.range.toString()
|
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
} else if (flags & flag) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_decorator,
|
|
|
|
decorator.range, decorator.name.range.toString()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
flags |= flag;
|
2018-07-19 02:10:04 +02:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
return flags;
|
2018-01-12 15:36:17 +01:00
|
|
|
}
|
|
|
|
|
2018-07-13 00:22:22 +02:00
|
|
|
/** Initializes a class declaration. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeClass(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: ClassDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** So far queued `extends` clauses. */
|
2018-04-11 23:35:19 +02:00
|
|
|
queuedExtends: ClassPrototype[],
|
2019-02-21 00:11:22 +01:00
|
|
|
/** So far queued `implements` clauses. */
|
|
|
|
queuedImplements: ClassPrototype[]
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var name = declaration.name.text;
|
|
|
|
var element = new ClassPrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
this.checkDecorators(declaration.decorators,
|
|
|
|
DecoratorFlags.GLOBAL |
|
|
|
|
DecoratorFlags.SEALED |
|
|
|
|
DecoratorFlags.UNMANAGED
|
|
|
|
)
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (!parent.add(name, element)) return;
|
2017-12-15 02:50:55 +01:00
|
|
|
|
2018-03-17 01:37:05 +01:00
|
|
|
var implementsTypes = declaration.implementsTypes;
|
2018-04-02 19:05:26 +02:00
|
|
|
if (implementsTypes) {
|
|
|
|
let numImplementsTypes = implementsTypes.length;
|
2019-02-21 00:11:22 +01:00
|
|
|
// cannot implement interfaces when unmanaged
|
|
|
|
if (element.hasDecorator(DecoratorFlags.UNMANAGED)) {
|
2018-04-03 23:56:48 +02:00
|
|
|
if (numImplementsTypes) {
|
2018-04-02 19:05:26 +02:00
|
|
|
this.error(
|
2018-04-11 23:35:19 +02:00
|
|
|
DiagnosticCode.Unmanaged_classes_cannot_implement_interfaces,
|
2018-04-02 19:05:26 +02:00
|
|
|
Range.join(
|
|
|
|
declaration.name.range,
|
|
|
|
implementsTypes[numImplementsTypes - 1].range
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else if (numImplementsTypes) {
|
2019-02-21 00:11:22 +01:00
|
|
|
// remember classes that implement interfaces
|
2018-07-10 00:34:40 +02:00
|
|
|
for (let i = 0; i < numImplementsTypes; ++i) {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.warning( // TODO: not yet supported
|
2018-07-10 00:34:40 +02:00
|
|
|
DiagnosticCode.Operation_not_supported,
|
|
|
|
implementsTypes[i].range
|
|
|
|
);
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
queuedImplements.push(element);
|
2018-03-17 01:37:05 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
// remember classes that extend another class
|
|
|
|
if (declaration.extendsType) queuedExtends.push(element);
|
2017-12-15 02:50:55 +01:00
|
|
|
|
2017-12-27 02:37:53 +01:00
|
|
|
// initialize members
|
2017-12-28 04:09:40 +01:00
|
|
|
var memberDeclarations = declaration.members;
|
2018-03-12 14:06:39 +01:00
|
|
|
for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
|
|
|
|
let memberDeclaration = memberDeclarations[i];
|
2018-01-02 03:54:06 +01:00
|
|
|
switch (memberDeclaration.kind) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.FIELDDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeField(<FieldDeclaration>memberDeclaration, element);
|
2017-12-15 02:50:55 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.METHODDECLARATION: {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (memberDeclaration.isAny(CommonFlags.GET | CommonFlags.SET)) {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeProperty(<MethodDeclaration>memberDeclaration, element);
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeMethod(<MethodDeclaration>memberDeclaration, element);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-12-07 14:33:32 +01:00
|
|
|
case NodeKind.INDEXSIGNATUREDECLARATION: break; // ignored for now
|
2019-02-21 00:11:22 +01:00
|
|
|
default: assert(false); // class member expected
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-07-13 00:22:22 +02:00
|
|
|
/** Initializes a field of a class or interface. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeField(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: FieldDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2018-04-24 23:11:11 +02:00
|
|
|
var decorators = declaration.decorators;
|
2019-02-21 00:11:22 +01:00
|
|
|
var element: DeclaredElement;
|
|
|
|
if (declaration.is(CommonFlags.STATIC)) { // global variable
|
|
|
|
assert(parent.kind != ElementKind.INTERFACE_PROTOTYPE);
|
|
|
|
element = new Global(
|
2018-03-20 23:41:37 +01:00
|
|
|
name,
|
2019-02-21 00:11:22 +01:00
|
|
|
parent,
|
|
|
|
this.checkDecorators(decorators,
|
|
|
|
(declaration.is(CommonFlags.READONLY)
|
|
|
|
? DecoratorFlags.INLINE
|
|
|
|
: DecoratorFlags.NONE
|
|
|
|
) | DecoratorFlags.LAZY
|
|
|
|
),
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
if (!parent.add(name, element)) return;
|
|
|
|
} else { // actual instance field
|
|
|
|
assert(!declaration.isAny(CommonFlags.ABSTRACT | CommonFlags.GET | CommonFlags.SET));
|
|
|
|
element = new FieldPrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
2018-04-24 23:11:11 +02:00
|
|
|
declaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
this.checkDecorators(decorators, DecoratorFlags.NONE)
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (!parent.addInstance(name, element)) return;
|
|
|
|
}
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-07-13 00:22:22 +02:00
|
|
|
/** Initializes a method of a class or interface. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeMethod(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: MethodDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var name = declaration.name.text;
|
|
|
|
var isStatic = declaration.is(CommonFlags.STATIC);
|
|
|
|
var acceptedFlags = DecoratorFlags.INLINE;
|
|
|
|
if (!declaration.is(CommonFlags.GENERIC)) {
|
|
|
|
acceptedFlags |= DecoratorFlags.OPERATOR_BINARY
|
|
|
|
| DecoratorFlags.OPERATOR_PREFIX
|
|
|
|
| DecoratorFlags.OPERATOR_POSTFIX;
|
|
|
|
}
|
|
|
|
var element = new FunctionPrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators, acceptedFlags)
|
|
|
|
);
|
|
|
|
if (isStatic) { // global function
|
2018-01-27 05:35:14 +01:00
|
|
|
assert(declaration.name.kind != NodeKind.CONSTRUCTOR);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (!parent.add(name, element)) return;
|
|
|
|
} else { // actual instance method
|
|
|
|
if (!parent.addInstance(name, element)) return;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
this.checkOperatorOverloads(declaration.decorators, element, parent);
|
2018-01-28 06:18:27 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Checks that operator overloads are generally valid, if present. */
|
2018-04-11 23:35:19 +02:00
|
|
|
private checkOperatorOverloads(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Decorators to check. */
|
2018-03-12 14:06:39 +01:00
|
|
|
decorators: DecoratorNode[] | null,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Decorated method. */
|
2018-02-25 00:13:39 +01:00
|
|
|
prototype: FunctionPrototype,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent class. */
|
2018-02-25 00:13:39 +01:00
|
|
|
classPrototype: ClassPrototype
|
2018-03-13 14:03:57 +01:00
|
|
|
): void {
|
2018-01-28 06:18:27 +01:00
|
|
|
if (decorators) {
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = decorators.length; i < k; ++i) {
|
|
|
|
let decorator = decorators[i];
|
2018-06-01 14:17:27 +02:00
|
|
|
switch (decorator.decoratorKind) {
|
|
|
|
case DecoratorKind.OPERATOR:
|
|
|
|
case DecoratorKind.OPERATOR_BINARY:
|
|
|
|
case DecoratorKind.OPERATOR_PREFIX:
|
|
|
|
case DecoratorKind.OPERATOR_POSTFIX: {
|
|
|
|
let numArgs = decorator.arguments && decorator.arguments.length || 0;
|
|
|
|
if (numArgs == 1) {
|
|
|
|
let firstArg = (<Expression[]>decorator.arguments)[0];
|
|
|
|
if (
|
|
|
|
firstArg.kind == NodeKind.LITERAL &&
|
|
|
|
(<LiteralExpression>firstArg).literalKind == LiteralKind.STRING
|
|
|
|
) {
|
|
|
|
let kind = operatorKindFromDecorator(
|
|
|
|
decorator.decoratorKind,
|
|
|
|
(<StringLiteralExpression>firstArg).value
|
2018-04-11 23:35:19 +02:00
|
|
|
);
|
2018-06-01 14:17:27 +02:00
|
|
|
if (kind == OperatorKind.INVALID) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
2018-06-01 14:17:27 +02:00
|
|
|
DiagnosticCode.Operation_not_supported,
|
2018-02-25 00:13:39 +01:00
|
|
|
firstArg.range
|
|
|
|
);
|
2018-04-11 23:35:19 +02:00
|
|
|
} else {
|
2018-06-01 14:17:27 +02:00
|
|
|
let overloads = classPrototype.overloadPrototypes;
|
|
|
|
if (overloads.has(kind)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_function_implementation,
|
|
|
|
firstArg.range
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
prototype.operatorKind = kind;
|
|
|
|
overloads.set(kind, prototype);
|
|
|
|
}
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-06-01 14:17:27 +02:00
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.String_literal_expected,
|
|
|
|
firstArg.range
|
|
|
|
);
|
2018-01-06 10:20:38 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
|
|
|
this.error(
|
2018-06-01 14:17:27 +02:00
|
|
|
DiagnosticCode.Expected_0_arguments_but_got_1,
|
2019-02-07 11:40:23 +01:00
|
|
|
decorator.range, "1", numArgs.toString(10)
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-06 10:20:38 +01:00
|
|
|
}
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Ensures that the property introduced by the specified getter or setter exists.*/
|
|
|
|
private ensureProperty(
|
|
|
|
/** The declaration of the getter or setter introducing the property. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: MethodDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype
|
|
|
|
): PropertyPrototype | null {
|
|
|
|
var name = declaration.name.text;
|
|
|
|
if (declaration.is(CommonFlags.STATIC)) {
|
|
|
|
let parentMembers = parent.members;
|
|
|
|
if (parentMembers && parentMembers.has(name)) {
|
|
|
|
let element = <Element>parentMembers.get(name)!;
|
|
|
|
if (element.kind == ElementKind.PROPERTY_PROTOTYPE) return <PropertyPrototype>element;
|
|
|
|
} else {
|
|
|
|
let element = new PropertyPrototype(name, parent, declaration);
|
|
|
|
if (!parent.add(name, element)) return null;
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let parentMembers = parent.instanceMembers;
|
|
|
|
if (parentMembers && parentMembers.has(name)) {
|
|
|
|
let element = <Element>parentMembers.get(name);
|
|
|
|
if (element.kind == ElementKind.PROPERTY_PROTOTYPE) return <PropertyPrototype>element;
|
|
|
|
} else {
|
|
|
|
let element = new PropertyPrototype(name, parent, declaration);
|
|
|
|
if (!parent.addInstance(name, element)) return null;
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_property_0,
|
|
|
|
declaration.name.range, name
|
|
|
|
);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Initializes a property of a class. */
|
|
|
|
private initializeProperty(
|
|
|
|
/** The declaration of the getter or setter. */
|
|
|
|
declaration: MethodDeclaration,
|
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var property = this.ensureProperty(declaration, parent);
|
|
|
|
if (!property) return;
|
|
|
|
var name = declaration.name.text;
|
2018-03-17 01:37:05 +01:00
|
|
|
var isGetter = declaration.is(CommonFlags.GET);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (isGetter) {
|
|
|
|
if (property.getterPrototype) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
2019-02-21 00:11:22 +01:00
|
|
|
DiagnosticCode.Duplicate_property_0,
|
|
|
|
declaration.name.range, name
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2017-12-27 02:37:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
if (property.setterPrototype) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
2019-02-21 00:11:22 +01:00
|
|
|
DiagnosticCode.Duplicate_property_0,
|
|
|
|
declaration.name.range, name
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2017-12-27 02:37:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
}
|
|
|
|
var element = new FunctionPrototype(
|
|
|
|
(isGetter ? GETTER_PREFIX : SETTER_PREFIX) + name,
|
|
|
|
property,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators,
|
|
|
|
DecoratorFlags.INLINE
|
|
|
|
)
|
|
|
|
);
|
|
|
|
if (isGetter) {
|
|
|
|
property.getterPrototype = element;
|
2017-12-27 02:37:53 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
property.setterPrototype = element;
|
2017-12-27 02:37:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes an enum. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeEnum(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: EnumDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var name = declaration.name.text;
|
|
|
|
var element = new Enum(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators,
|
|
|
|
DecoratorFlags.GLOBAL |
|
|
|
|
DecoratorFlags.INLINE |
|
|
|
|
DecoratorFlags.LAZY
|
|
|
|
)
|
|
|
|
);
|
|
|
|
if (!parent.add(name, element)) return;
|
2017-12-28 04:09:40 +01:00
|
|
|
var values = declaration.values;
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = values.length; i < k; ++i) {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.initializeEnumValue(values[i], element);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes an enum value. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeEnumValue(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: EnumValueDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent enum. */
|
|
|
|
parent: Enum
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2019-02-21 00:11:22 +01:00
|
|
|
var element = new EnumValue(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators,
|
|
|
|
DecoratorFlags.NONE
|
|
|
|
)
|
|
|
|
);
|
|
|
|
if (!parent.add(name, element)) return;
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes an `export` statement. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeExports(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The statement to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
statement: ExportStatement,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent file. */
|
|
|
|
parent: File,
|
|
|
|
/** So far queued `export`s. */
|
|
|
|
queuedExports: Map<File,Map<string,QueuedExport>>,
|
|
|
|
/** So far queued `export *`s. */
|
|
|
|
queuedExportsStar: Map<File,QueuedExportStar[]>
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2017-12-28 04:09:40 +01:00
|
|
|
var members = statement.members;
|
2019-02-21 00:11:22 +01:00
|
|
|
if (members) { // export { foo, bar } [from "./baz"]
|
2018-06-12 00:45:19 +02:00
|
|
|
for (let i = 0, k = members.length; i < k; ++i) {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeExport(members[i], parent, statement.internalPath, queuedExports);
|
2018-06-12 00:45:19 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
} else { // export * from "./baz"
|
|
|
|
let queued: QueuedExportStar[];
|
|
|
|
if (queuedExportsStar.has(parent)) queued = queuedExportsStar.get(parent)!;
|
|
|
|
else queuedExportsStar.set(parent, queued = []);
|
|
|
|
let foreignPath = assert(statement.internalPath); // must be set for export *
|
|
|
|
queued.push(new QueuedExportStar(
|
|
|
|
foreignPath,
|
|
|
|
foreignPath.endsWith(INDEX_SUFFIX) // strip or add index depending on what's already present
|
|
|
|
? foreignPath.substring(0, foreignPath.length - INDEX_SUFFIX.length)
|
|
|
|
: foreignPath + INDEX_SUFFIX,
|
|
|
|
assert(statement.path)
|
|
|
|
));
|
2018-02-09 02:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a single `export` member. Does not handle `export *`. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeExport(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The member to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
member: ExportMember,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Local file. */
|
|
|
|
localFile: File,
|
|
|
|
/** Path to the other file, if present. */
|
|
|
|
foreignPath: string | null,
|
|
|
|
/** So far queued `export`s. */
|
|
|
|
queuedExports: Map<File,Map<string,QueuedExport>>
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var localName = member.localName.text;
|
|
|
|
var foreignName = member.exportedName.text;
|
|
|
|
|
|
|
|
// check for duplicates
|
|
|
|
var element = localFile.lookupExport(foreignName);
|
|
|
|
if (element) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
2019-02-21 00:11:22 +01:00
|
|
|
member.exportedName.range, foreignName
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2017-11-17 14:33:51 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
// local element, i.e. export { foo [as bar] }
|
|
|
|
if (foreignPath === null) {
|
2017-12-02 01:14:15 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// resolve right away if the local element already exists
|
|
|
|
if (element = localFile.lookupInSelf(localName)) {
|
|
|
|
localFile.ensureExport(foreignName, element);
|
2017-12-02 01:14:15 +01:00
|
|
|
|
|
|
|
// otherwise queue it
|
2019-02-21 00:11:22 +01:00
|
|
|
} else {
|
|
|
|
let queued: Map<string,QueuedExport>;
|
|
|
|
if (queuedExports.has(localFile)) queued = queuedExports.get(localFile)!;
|
|
|
|
else queuedExports.set(localFile, queued = new Map());
|
|
|
|
queued.set(foreignName, new QueuedExport(
|
|
|
|
member.localName,
|
|
|
|
member.exportedName,
|
|
|
|
null, null
|
|
|
|
));
|
2017-12-02 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// foreign element, i.e. export { foo } from "./bar"
|
2017-11-17 14:33:51 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
let queued: Map<string,QueuedExport>;
|
|
|
|
if (queuedExports.has(localFile)) queued = queuedExports.get(localFile)!;
|
|
|
|
else queuedExports.set(localFile, queued = new Map());
|
|
|
|
queued.set(foreignName, new QueuedExport(
|
|
|
|
member.localName,
|
|
|
|
member.exportedName,
|
|
|
|
foreignPath,
|
|
|
|
foreignPath.endsWith(INDEX_SUFFIX) // strip or add index depending on what's already present
|
|
|
|
? foreignPath.substring(0, foreignPath.length - INDEX_SUFFIX.length)
|
|
|
|
: foreignPath + INDEX_SUFFIX
|
|
|
|
));
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes an `import` statement. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeImports(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The statement to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
statement: ImportStatement,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent file. */
|
|
|
|
parent: File,
|
|
|
|
/** So far queued `import`s. */
|
|
|
|
queuedImports: QueuedImport[],
|
|
|
|
/** SO far queued `export`s. */
|
|
|
|
queuedExports: Map<File,Map<string,QueuedExport>>
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2017-12-28 04:09:40 +01:00
|
|
|
var declarations = statement.declarations;
|
2019-02-21 00:11:22 +01:00
|
|
|
if (declarations) { // import { foo [as bar] } from "./baz"
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = declarations.length; i < k; ++i) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.initializeImport(
|
|
|
|
declarations[i],
|
2019-02-21 00:11:22 +01:00
|
|
|
parent,
|
2018-02-25 00:13:39 +01:00
|
|
|
statement.internalPath,
|
2019-02-21 00:11:22 +01:00
|
|
|
queuedImports,
|
|
|
|
queuedExports
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-06-12 00:45:19 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
} else if (statement.namespaceName) { // import * as foo from "./bar"
|
|
|
|
queuedImports.push(new QueuedImport(
|
|
|
|
parent,
|
|
|
|
statement.namespaceName,
|
|
|
|
null, // indicates import *
|
|
|
|
statement.internalPath,
|
|
|
|
statement.internalPath + INDEX_SUFFIX
|
|
|
|
));
|
|
|
|
} else {
|
|
|
|
// import "./foo"
|
2018-01-05 18:19:32 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a single `import` declaration. Does not handle `import *`. */
|
|
|
|
private initializeImport( // { foo [as bar] }
|
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: ImportDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent file. */
|
|
|
|
parent: File,
|
|
|
|
/** Path to the other file. */
|
|
|
|
foreignPath: string,
|
|
|
|
/** So far queued `import`s. */
|
|
|
|
queuedImports: QueuedImport[],
|
|
|
|
/** So far queued `export`s. */
|
|
|
|
queuedExports: Map<File,Map<string,QueuedExport>>
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var foreignPathAlt = foreignPath.endsWith(INDEX_SUFFIX) // strip or add index depending on what's already present
|
|
|
|
? foreignPath.substring(0, foreignPath.length - INDEX_SUFFIX.length)
|
|
|
|
: foreignPath + INDEX_SUFFIX;
|
|
|
|
|
|
|
|
// resolve right away if the element exists
|
|
|
|
var element = this.lookupForeign(declaration.foreignName.text, foreignPath, foreignPathAlt, queuedExports);
|
|
|
|
if (element) {
|
|
|
|
parent.add(declaration.name.text, element, true);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise queue it
|
2019-02-21 00:11:22 +01:00
|
|
|
queuedImports.push(new QueuedImport(
|
|
|
|
parent,
|
|
|
|
declaration.name,
|
|
|
|
declaration.foreignName,
|
|
|
|
foreignPath,
|
|
|
|
foreignPathAlt
|
|
|
|
));
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a function. Does not handle methods. */
|
|
|
|
private initializeFunction(
|
|
|
|
/** The declaration to initialize. */
|
|
|
|
declaration: FunctionDeclaration,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element
|
|
|
|
): void {
|
|
|
|
var name = declaration.name.text;
|
|
|
|
var validDecorators = DecoratorFlags.NONE;
|
2019-02-22 08:25:41 +01:00
|
|
|
if (declaration.is(CommonFlags.AMBIENT)) {
|
|
|
|
validDecorators |= DecoratorFlags.EXTERNAL;
|
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
validDecorators |= DecoratorFlags.INLINE;
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
2019-02-22 08:25:41 +01:00
|
|
|
if (!declaration.is(CommonFlags.INSTANCE)) {
|
|
|
|
if (parent.kind != ElementKind.CLASS_PROTOTYPE) {
|
|
|
|
validDecorators |= DecoratorFlags.GLOBAL;
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
}
|
|
|
|
if (!declaration.is(CommonFlags.GENERIC)) {
|
2019-02-22 08:25:41 +01:00
|
|
|
if (parent.kind == ElementKind.FILE && (<File>parent).source.isEntry) {
|
2019-02-21 00:11:22 +01:00
|
|
|
validDecorators |= DecoratorFlags.START;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var element = new FunctionPrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
this.checkDecorators(declaration.decorators, validDecorators)
|
2018-04-11 23:35:19 +02:00
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
if (!parent.add(name, element)) return;
|
|
|
|
if (element.hasDecorator(DecoratorFlags.START)) {
|
|
|
|
if (this.explicitStartFunction) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
2019-02-21 00:11:22 +01:00
|
|
|
DiagnosticCode.Module_cannot_have_multiple_start_functions,
|
|
|
|
assert(findDecorator(DecoratorKind.START, declaration.decorators)).range
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
} else this.explicitStartFunction = element;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
}
|
2017-12-13 23:24:13 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes an interface. */
|
|
|
|
private initializeInterface(
|
|
|
|
/** The declaration to initialize. */
|
|
|
|
declaration: InterfaceDeclaration,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element
|
|
|
|
): void {
|
|
|
|
var name = declaration.name.text;
|
|
|
|
var element = new InterfacePrototype(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators,
|
|
|
|
DecoratorFlags.GLOBAL
|
|
|
|
)
|
|
|
|
);
|
|
|
|
if (!parent.add(name, element)) return;
|
2017-12-28 04:09:40 +01:00
|
|
|
var memberDeclarations = declaration.members;
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
|
|
|
|
let memberDeclaration = memberDeclarations[i];
|
2018-01-02 03:54:06 +01:00
|
|
|
switch (memberDeclaration.kind) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.FIELDDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeField(<FieldDeclaration>memberDeclaration, element);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.METHODDECLARATION: {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (memberDeclaration.isAny(CommonFlags.GET | CommonFlags.SET)) {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeProperty(<MethodDeclaration>memberDeclaration, element);
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeMethod(<MethodDeclaration>memberDeclaration, element);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
default: assert(false); // interface member expected
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a namespace. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeNamespace(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** The declaration to initialize. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: NamespaceDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent element, usually a file or another namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** So far queued `extends` clauses. */
|
2018-04-11 23:35:19 +02:00
|
|
|
queuedExtends: ClassPrototype[],
|
2019-02-21 00:11:22 +01:00
|
|
|
/** So far queued `implements` clauses. */
|
|
|
|
queuedImplements: ClassPrototype[]
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2019-02-21 00:11:22 +01:00
|
|
|
var name = declaration.name.text;
|
|
|
|
var element = new Namespace(name, parent, declaration);
|
|
|
|
if (!parent.add(name, element)) return;
|
|
|
|
element = assert(parent.lookupInSelf(name)); // possibly merged
|
2017-12-28 04:09:40 +01:00
|
|
|
var members = declaration.members;
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = members.length; i < k; ++i) {
|
2017-12-15 02:50:55 +01:00
|
|
|
switch (members[i].kind) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.CLASSDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeClass(<ClassDeclaration>members[i], element, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.ENUMDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeEnum(<EnumDeclaration>members[i], element);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.FUNCTIONDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeFunction(<FunctionDeclaration>members[i], element);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.INTERFACEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeInterface(<InterfaceDeclaration>members[i], element);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.NAMESPACEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeNamespace(<NamespaceDeclaration>members[i], element, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.TYPEDECLARATION: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeTypeDefinition(<TypeDeclaration>members[i], element);
|
2017-12-19 17:49:15 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.VARIABLE: {
|
2019-02-21 00:11:22 +01:00
|
|
|
this.initializeVariables(<VariableStatement>members[i], element);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
default: assert(false); // namespace member expected
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a `type` definition. */
|
|
|
|
private initializeTypeDefinition(
|
|
|
|
/** The declaration to initialize. */
|
|
|
|
declaration: TypeDeclaration,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element
|
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2019-02-21 00:11:22 +01:00
|
|
|
var element = new TypeDefinition(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
this.checkDecorators(declaration.decorators, DecoratorFlags.NONE)
|
|
|
|
);
|
|
|
|
parent.add(name, element); // reports
|
2017-12-19 17:49:15 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Initializes a variable statement. */
|
|
|
|
private initializeVariables(
|
|
|
|
/** The statement to initialize. */
|
|
|
|
statement: VariableStatement,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element
|
|
|
|
): void {
|
2017-12-28 04:09:40 +01:00
|
|
|
var declarations = statement.declarations;
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = declarations.length; i < k; ++i) {
|
|
|
|
let declaration = declarations[i];
|
2019-02-21 00:11:22 +01:00
|
|
|
let name = declaration.name.text;
|
|
|
|
let acceptedFlags = DecoratorFlags.GLOBAL | DecoratorFlags.LAZY;
|
|
|
|
if (declaration.is(CommonFlags.DECLARE)) {
|
|
|
|
acceptedFlags |= DecoratorFlags.EXTERNAL;
|
2018-07-19 16:38:09 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
if (declaration.is(CommonFlags.CONST)) {
|
|
|
|
acceptedFlags |= DecoratorFlags.INLINE;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
let element = new Global(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
this.checkDecorators(declaration.decorators, acceptedFlags),
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
if (!parent.add(name, element)) continue; // reports
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-01-03 18:33:27 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Indicates the specific kind of an {@link Element}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
export enum ElementKind {
|
2017-12-16 17:54:53 +01:00
|
|
|
/** A {@link Global}. */
|
|
|
|
GLOBAL,
|
|
|
|
/** A {@link Local}. */
|
|
|
|
LOCAL,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** An {@link Enum}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
ENUM,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** An {@link EnumValue}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
ENUMVALUE,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** A {@link FunctionPrototype}. */
|
2017-11-17 14:33:51 +01:00
|
|
|
FUNCTION_PROTOTYPE,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** A {@link Function}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
FUNCTION,
|
2018-03-12 22:34:40 +01:00
|
|
|
/** A {@link FunctionTarget}. */
|
|
|
|
FUNCTION_TARGET,
|
2017-12-16 17:54:53 +01:00
|
|
|
/** A {@link ClassPrototype}. */
|
|
|
|
CLASS_PROTOTYPE,
|
|
|
|
/** A {@link Class}. */
|
|
|
|
CLASS,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** An {@link InterfacePrototype}. */
|
2017-11-17 14:33:51 +01:00
|
|
|
INTERFACE_PROTOTYPE,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** An {@link Interface}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
INTERFACE,
|
2017-12-16 17:54:53 +01:00
|
|
|
/** A {@link FieldPrototype}. */
|
|
|
|
FIELD_PROTOTYPE,
|
|
|
|
/** A {@link Field}. */
|
|
|
|
FIELD,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A {@link PropertyPrototype}. */
|
|
|
|
PROPERTY_PROTOTYPE,
|
2018-01-17 02:08:14 +01:00
|
|
|
/** A {@link Property}. */
|
2017-12-16 17:54:53 +01:00
|
|
|
PROPERTY,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** A {@link Namespace}. */
|
2018-06-12 00:45:19 +02:00
|
|
|
NAMESPACE,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A {@link File}. */
|
|
|
|
FILE,
|
|
|
|
/** A {@link TypeDefinition}. */
|
|
|
|
TYPEDEFINITION,
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Indicates built-in decorators that are present. */
|
2018-04-11 23:35:19 +02:00
|
|
|
export enum DecoratorFlags {
|
|
|
|
/** No flags set. */
|
|
|
|
NONE = 0,
|
|
|
|
/** Is a program global. */
|
|
|
|
GLOBAL = 1 << 0,
|
2018-06-01 14:17:27 +02:00
|
|
|
/** Is a binary operator overload. */
|
|
|
|
OPERATOR_BINARY = 1 << 1,
|
|
|
|
/** Is a unary prefix operator overload. */
|
|
|
|
OPERATOR_PREFIX = 1 << 2,
|
|
|
|
/** Is a unary postfix operator overload. */
|
|
|
|
OPERATOR_POSTFIX = 1 << 3,
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Is an unmanaged class. */
|
2018-06-01 14:17:27 +02:00
|
|
|
UNMANAGED = 1 << 4,
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Is a sealed class. */
|
2018-06-01 14:17:27 +02:00
|
|
|
SEALED = 1 << 5,
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Is always inlined. */
|
2018-06-28 01:44:32 +02:00
|
|
|
INLINE = 1 << 6,
|
|
|
|
/** Is using a different external name. */
|
2018-07-19 02:10:04 +02:00
|
|
|
EXTERNAL = 1 << 7,
|
|
|
|
/** Is a builtin. */
|
2019-02-21 00:11:22 +01:00
|
|
|
BUILTIN = 1 << 8,
|
|
|
|
/** Is compiled lazily. */
|
|
|
|
LAZY = 1 << 9,
|
|
|
|
/** Is the explicit start function. */
|
|
|
|
START = 1 << 10
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Translates a decorator kind to the respective decorator flag. */
|
2018-04-11 23:35:19 +02:00
|
|
|
export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
|
|
|
|
switch (kind) {
|
|
|
|
case DecoratorKind.GLOBAL: return DecoratorFlags.GLOBAL;
|
2018-06-01 14:17:27 +02:00
|
|
|
case DecoratorKind.OPERATOR:
|
|
|
|
case DecoratorKind.OPERATOR_BINARY: return DecoratorFlags.OPERATOR_BINARY;
|
|
|
|
case DecoratorKind.OPERATOR_PREFIX: return DecoratorFlags.OPERATOR_PREFIX;
|
|
|
|
case DecoratorKind.OPERATOR_POSTFIX: return DecoratorFlags.OPERATOR_POSTFIX;
|
2018-04-11 23:35:19 +02:00
|
|
|
case DecoratorKind.UNMANAGED: return DecoratorFlags.UNMANAGED;
|
|
|
|
case DecoratorKind.SEALED: return DecoratorFlags.SEALED;
|
|
|
|
case DecoratorKind.INLINE: return DecoratorFlags.INLINE;
|
2018-06-28 01:44:32 +02:00
|
|
|
case DecoratorKind.EXTERNAL: return DecoratorFlags.EXTERNAL;
|
2018-07-19 02:10:04 +02:00
|
|
|
case DecoratorKind.BUILTIN: return DecoratorFlags.BUILTIN;
|
2019-02-21 00:11:22 +01:00
|
|
|
case DecoratorKind.LAZY: return DecoratorFlags.LAZY;
|
|
|
|
case DecoratorKind.START: return DecoratorFlags.START;
|
2018-04-11 23:35:19 +02:00
|
|
|
default: return DecoratorFlags.NONE;
|
|
|
|
}
|
2017-12-13 04:46:05 +01:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** Base class of all program elements. */
|
2017-10-19 18:55:27 +02:00
|
|
|
export abstract class Element {
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent element. */
|
|
|
|
parent: Element;
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Common flags indicating specific traits. */
|
|
|
|
flags: CommonFlags = CommonFlags.NONE;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Decorator flags indicating annotated traits. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE;
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Member elements. */
|
|
|
|
members: Map<string,DeclaredElement> | null = null;
|
|
|
|
/** Shadowing type in type space, if any. */
|
|
|
|
shadowType: TypeDefinition | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new program element. */
|
|
|
|
protected constructor(
|
|
|
|
/** Specific element kind. */
|
|
|
|
public kind: ElementKind,
|
|
|
|
/** Simple name. */
|
|
|
|
public name: string,
|
|
|
|
/** Internal name referring to this element. */
|
|
|
|
public internalName: string,
|
|
|
|
/** Containing {@link Program}. */
|
|
|
|
public program: Program,
|
|
|
|
/** Parent element. */
|
|
|
|
parent: Element | null
|
|
|
|
) {
|
2017-10-19 18:55:27 +02:00
|
|
|
this.program = program;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.name = name;
|
2017-10-19 18:55:27 +02:00
|
|
|
this.internalName = internalName;
|
2019-02-21 00:11:22 +01:00
|
|
|
if (parent) {
|
|
|
|
this.parent = parent;
|
|
|
|
} else {
|
|
|
|
assert(this.kind == ElementKind.FILE);
|
|
|
|
this.parent = this; // special case to keep this.parent non-nullable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the enclosing file. */
|
|
|
|
get file(): File {
|
|
|
|
var current: Element = this;
|
|
|
|
do if ((current = current.parent).kind == ElementKind.FILE) return <File>current;
|
|
|
|
while (true);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Tests if this element has a specific flag or flags. */
|
2018-03-17 01:37:05 +01:00
|
|
|
is(flag: CommonFlags): bool { return (this.flags & flag) == flag; }
|
2018-03-17 14:40:58 +01:00
|
|
|
/** Tests if this element has any of the specified flags. */
|
|
|
|
isAny(flags: CommonFlags): bool { return (this.flags & flags) != 0; }
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Sets a specific flag or flags. */
|
2018-03-17 01:37:05 +01:00
|
|
|
set(flag: CommonFlags): void { this.flags |= flag; }
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Tests if this element has a specific decorator flag or flags. */
|
|
|
|
hasDecorator(flag: DecoratorFlags): bool { return (this.decoratorFlags & flag) == flag; }
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Looks up the element with the specified name within this element. */
|
|
|
|
lookupInSelf(name: string): DeclaredElement | null {
|
|
|
|
var members = this.members;
|
|
|
|
if (members && members.has(name)) return members.get(name)!;
|
|
|
|
return null;
|
|
|
|
}
|
2018-06-12 00:45:19 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Looks up the element with the specified name relative to this element, like in JS. */
|
|
|
|
abstract lookup(name: string): Element | null;
|
2018-06-12 00:45:19 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Adds an element as a member of this one. Reports and returns `false` if a duplicate. */
|
|
|
|
add(name: string, element: DeclaredElement): bool {
|
|
|
|
var originalDeclaration = element.declaration;
|
|
|
|
var members = this.members;
|
|
|
|
if (!members) this.members = members = new Map();
|
|
|
|
else if (members.has(name)) {
|
|
|
|
let actual = members.get(name)!;
|
|
|
|
if (actual.parent !== this) {
|
|
|
|
// override non-own element
|
|
|
|
} else {
|
|
|
|
let merged = tryMerge(actual, element);
|
|
|
|
if (merged) {
|
|
|
|
element = merged; // use merged element
|
|
|
|
} else {
|
|
|
|
this.program.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
element.identifierNode.range, element.identifierNode.text
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
members.set(name, element);
|
|
|
|
var program = this.program;
|
|
|
|
if (element.kind != ElementKind.FUNCTION_PROTOTYPE || !(<FunctionPrototype>element).isBound) {
|
|
|
|
// prefer unbound prototypes in global lookup maps
|
|
|
|
program.elementsByName.set(element.internalName, element);
|
|
|
|
program.elementsByDeclaration.set(originalDeclaration, element);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2018-06-12 00:45:19 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Returns a string representation of this element. */
|
|
|
|
toString(): string {
|
|
|
|
return ElementKind[this.kind] + ":" + this.internalName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Base class of elements with an associated declaration statement. */
|
|
|
|
export abstract class DeclaredElement extends Element {
|
|
|
|
|
|
|
|
/** Constructs a new declared program element. */
|
|
|
|
protected constructor(
|
|
|
|
/** Specific element kind. */
|
|
|
|
kind: ElementKind,
|
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Internal name referring to this element. */
|
|
|
|
internalName: string,
|
|
|
|
/** Containing {@link Program}. */
|
2018-06-12 00:45:19 +02:00
|
|
|
program: Program,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent element. */
|
|
|
|
parent: Element | null,
|
|
|
|
/** Declaration reference. */
|
|
|
|
public declaration: DeclarationStatement
|
2018-06-12 00:45:19 +02:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(kind, name, internalName, program, parent);
|
|
|
|
// It is necessary to have access to identifiers of all members and exports
|
|
|
|
// for reporting purposes and this is the lowest common denominator. Comes
|
|
|
|
// at the expense of not having more specific type information in derived
|
|
|
|
// classes, though. Instead, derived classes implement getters for other
|
|
|
|
// important AST nodes directly through manual casting, allowing the resolver
|
|
|
|
// etc. to not worry about actual declarations.
|
|
|
|
this.declaration = declaration;
|
|
|
|
this.flags = declaration.flags; // inherit
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tests if this element is a library element. */
|
|
|
|
get isDeclaredInLibrary(): bool {
|
|
|
|
return this.declaration.range.source.isLibrary;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated identifier node. */
|
|
|
|
get identifierNode(): IdentifierExpression {
|
|
|
|
return this.declaration.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the assiciated decorator nodes. */
|
|
|
|
get decoratorNodes(): DecoratorNode[] | null {
|
|
|
|
return this.declaration.decorators;
|
2018-06-12 00:45:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Base class of elements that can be resolved to a concrete type. */
|
|
|
|
export abstract class TypedElement extends DeclaredElement {
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Resolved type. Set once `is(RESOLVED)`, otherwise void. */
|
|
|
|
type: Type = Type.void;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Sets the resolved type of this element. */
|
|
|
|
setType(type: Type): void {
|
|
|
|
assert(!this.is(CommonFlags.RESOLVED));
|
|
|
|
this.type = type;
|
|
|
|
this.set(CommonFlags.RESOLVED);
|
|
|
|
}
|
|
|
|
}
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A file representing the implicit top-level namespace of a source. */
|
|
|
|
export class File extends Element {
|
|
|
|
|
|
|
|
/** File exports. */
|
|
|
|
exports: Map<string,DeclaredElement> | null = null;
|
|
|
|
/** File re-exports. */
|
|
|
|
exportsStar: File[] | null = null;
|
|
|
|
/** Top-level start function of this file. */
|
|
|
|
startFunction: Function;
|
|
|
|
|
|
|
|
/** Constructs a new file. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Program this file belongs to. */
|
2018-02-25 00:13:39 +01:00
|
|
|
program: Program,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Source of this file. */
|
|
|
|
public source: Source
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.FILE,
|
|
|
|
source.normalizedPath,
|
|
|
|
source.internalPath,
|
|
|
|
program,
|
|
|
|
null // special case for files
|
|
|
|
);
|
|
|
|
this.source = source;
|
|
|
|
assert(!program.filesByName.has(this.internalName));
|
|
|
|
program.filesByName.set(this.internalName, this);
|
|
|
|
var startFunction = this.program.makeNativeFunction(
|
|
|
|
"start:" + this.internalName,
|
|
|
|
new Signature(null, Type.void),
|
|
|
|
this
|
|
|
|
);
|
|
|
|
startFunction.internalName = startFunction.name;
|
|
|
|
this.startFunction = startFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
add(name: string, element: DeclaredElement, isImport: bool = false): bool {
|
|
|
|
if (!super.add(name, element)) return false;
|
|
|
|
element = assert(this.lookupInSelf(name)); // possibly merged
|
|
|
|
if (element.is(CommonFlags.EXPORT) && !isImport) {
|
|
|
|
this.ensureExport(
|
|
|
|
element.name,
|
|
|
|
element
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (element.hasDecorator(DecoratorFlags.GLOBAL)) this.program.ensureGlobal(name, element);
|
|
|
|
return true;
|
2017-11-17 14:33:51 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookupInSelf(name: string): DeclaredElement | null {
|
|
|
|
var element = super.lookupInSelf(name);
|
|
|
|
if (element) return element;
|
|
|
|
var exportsStar = this.exportsStar;
|
|
|
|
if (exportsStar) {
|
|
|
|
for (let i = 0, k = exportsStar.length; i < k; ++i) {
|
|
|
|
if (element = exportsStar[i].lookupInSelf(name)) return element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
var element = this.lookupInSelf(name);
|
|
|
|
if (element) return element;
|
|
|
|
return this.program.lookupGlobal(name);
|
|
|
|
}
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Ensures that an element is an export of this file. */
|
|
|
|
ensureExport(name: string, element: DeclaredElement): void {
|
|
|
|
var exports = this.exports;
|
|
|
|
if (!exports) this.exports = exports = new Map();
|
|
|
|
exports.set(name, element);
|
|
|
|
if (this.source.isLibrary) this.program.ensureGlobal(name, element);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Ensures that another file is a re-export of this file. */
|
|
|
|
ensureExportStar(file: File): void {
|
|
|
|
var exportsStar = this.exportsStar;
|
|
|
|
if (!exportsStar) this.exportsStar = exportsStar = [];
|
|
|
|
else if (exportsStar.includes(file)) return;
|
|
|
|
exportsStar.push(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Looks up the export of the specified name. */
|
|
|
|
lookupExport(name: string): DeclaredElement | null {
|
|
|
|
var exports = this.exports;
|
|
|
|
if (exports && exports.has(name)) return exports.get(name)!;
|
|
|
|
var exportsStar = this.exportsStar;
|
|
|
|
if (exportsStar) {
|
|
|
|
for (let i = 0, k = exportsStar.length; i < k; ++i) {
|
|
|
|
let element = exportsStar[i].lookupExport(name);
|
|
|
|
if (element) return element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Creates an imported namespace from this file. */
|
|
|
|
asImportedNamespace(name: string, parent: Element): Namespace {
|
|
|
|
var ns = new Namespace(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
this.program.makeNativeNamespaceDeclaration(name)
|
|
|
|
);
|
|
|
|
var exports = this.exports;
|
|
|
|
if (exports) {
|
|
|
|
for (let [memberName, member] of exports) {
|
|
|
|
ns.add(memberName, member);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ns;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** A type definition. */
|
|
|
|
export class TypeDefinition extends TypedElement {
|
|
|
|
|
|
|
|
/** Constructs a new type definition. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: TypeDeclaration,
|
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.TYPEDEFINITION,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, false),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
this.decoratorFlags = decoratorFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated type parameter nodes. */
|
|
|
|
get typeParameterNodes(): TypeParameterNode[] | null {
|
|
|
|
return (<TypeDeclaration>this.declaration).typeParameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated type node. */
|
|
|
|
get typeNode(): CommonTypeNode {
|
|
|
|
return (<TypeDeclaration>this.declaration).type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A namespace that differs from a file in being user-declared with a name. */
|
|
|
|
export class Namespace extends DeclaredElement {
|
|
|
|
|
|
|
|
/** Constructs a new namespace. */
|
|
|
|
constructor(
|
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file or another namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: NamespaceDeclaration
|
|
|
|
) {
|
|
|
|
super(
|
|
|
|
ElementKind.NAMESPACE,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, false),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.lookupInSelf(name)
|
|
|
|
|| this.parent.lookup(name);
|
|
|
|
}
|
|
|
|
}
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** An enum. */
|
|
|
|
export class Enum extends TypedElement {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new enum. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: EnumDeclaration,
|
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.ENUM,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, false),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
this.decoratorFlags = decoratorFlags;
|
|
|
|
this.setType(Type.i32);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.lookupInSelf(name)
|
|
|
|
|| this.parent.lookup(name);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Indicates the kind of an inlined constant value. */
|
2018-02-14 09:18:43 +01:00
|
|
|
export const enum ConstantValueKind {
|
2019-02-21 00:11:22 +01:00
|
|
|
/** No constant value. */
|
2018-02-14 09:18:43 +01:00
|
|
|
NONE,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constant integer value. */
|
2018-02-14 09:18:43 +01:00
|
|
|
INTEGER,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constant float value. */
|
2018-02-14 09:18:43 +01:00
|
|
|
FLOAT
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Base class of all variable-like program elements. */
|
|
|
|
export abstract class VariableLikeElement extends TypedElement {
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2018-02-14 09:18:43 +01:00
|
|
|
/** Constant value kind. */
|
|
|
|
constantValueKind: ConstantValueKind = ConstantValueKind.NONE;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constant integer value, if applicable. */
|
2018-02-14 09:18:43 +01:00
|
|
|
constantIntegerValue: I64;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constant float value, if applicable. */
|
2018-02-14 09:18:43 +01:00
|
|
|
constantFloatValue: f64;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new variable-like element. */
|
2018-04-03 23:56:48 +02:00
|
|
|
protected constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Specific element kind. */
|
|
|
|
kind: ElementKind,
|
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file, namespace or class. */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. Creates a native declaration if omitted. */
|
|
|
|
declaration: VariableLikeDeclarationStatement = parent.program.makeNativeVariableDeclaration(name)
|
2018-04-03 23:56:48 +02:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
kind,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, false),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
this.flags = declaration.flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated type node.s */
|
|
|
|
get typeNode(): CommonTypeNode | null {
|
|
|
|
return (<VariableLikeDeclarationStatement>this.declaration).type;
|
2018-04-03 23:56:48 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Gets the associated initializer node. */
|
|
|
|
get initializerNode(): Expression | null {
|
|
|
|
return (<VariableLikeDeclarationStatement>this.declaration).initializer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Applies a constant integer value to this element. */
|
|
|
|
setConstantIntegerValue(value: I64, type: Type): void {
|
|
|
|
assert(type.is(TypeFlags.INTEGER));
|
|
|
|
this.type = type;
|
2018-02-14 09:18:43 +01:00
|
|
|
this.constantValueKind = ConstantValueKind.INTEGER;
|
2018-06-29 00:14:42 +02:00
|
|
|
this.constantIntegerValue = value;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.set(CommonFlags.CONST | CommonFlags.INLINED | CommonFlags.RESOLVED);
|
2017-12-28 15:17:35 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Applies a constant float value to this element. */
|
|
|
|
setConstantFloatValue(value: f64, type: Type): void {
|
|
|
|
assert(type.is(TypeFlags.FLOAT));
|
|
|
|
this.type = type;
|
2018-02-14 09:18:43 +01:00
|
|
|
this.constantValueKind = ConstantValueKind.FLOAT;
|
2017-12-28 15:17:35 +01:00
|
|
|
this.constantFloatValue = value;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.set(CommonFlags.CONST | CommonFlags.INLINED | CommonFlags.RESOLVED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** An enum value. */
|
|
|
|
export class EnumValue extends VariableLikeElement {
|
|
|
|
|
|
|
|
/** Constructs a new enum value. */
|
|
|
|
constructor(
|
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent enum. */
|
|
|
|
parent: Enum,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: EnumValueDeclaration,
|
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
|
|
|
) {
|
|
|
|
super(
|
|
|
|
ElementKind.ENUMVALUE,
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
this.decoratorFlags = decoratorFlags;
|
|
|
|
this.setType(Type.i32);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated value node. */
|
|
|
|
get valueNode(): Expression | null {
|
|
|
|
return (<EnumValueDeclaration>this.declaration).value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2017-12-28 15:17:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** A global variable. */
|
|
|
|
export class Global extends VariableLikeElement {
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new global variable. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file, namespace or static class. */
|
|
|
|
parent: Element,
|
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags,
|
|
|
|
/** Declaration reference. Creates a native declaration if omitted. */
|
|
|
|
declaration: VariableLikeDeclarationStatement = parent.program.makeNativeVariableDeclaration(name)
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.GLOBAL,
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
2018-04-24 23:11:11 +02:00
|
|
|
this.decoratorFlags = decoratorFlags;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A function parameter. */
|
2017-10-19 18:55:27 +02:00
|
|
|
export class Parameter {
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new function parameter. */
|
2019-02-21 00:11:22 +01:00
|
|
|
constructor(
|
|
|
|
/** Parameter name. */
|
|
|
|
public name: string,
|
|
|
|
/** Parameter type. */
|
|
|
|
public type: Type,
|
|
|
|
/** Parameter initializer, if present. */
|
|
|
|
public initializer: Expression | null = null
|
|
|
|
) {}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A local variable. */
|
2017-12-28 15:17:35 +01:00
|
|
|
export class Local extends VariableLikeElement {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new local variable. */
|
2018-04-03 23:56:48 +02:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Zero-based index within the enclosing function. `-1` indicates a virtual local. */
|
|
|
|
public index: i32,
|
|
|
|
/** Resolved type. */
|
2018-04-03 23:56:48 +02:00
|
|
|
type: Type,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent function. */
|
|
|
|
parent: Function,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: VariableLikeDeclarationStatement = parent.program.makeNativeVariableDeclaration(name)
|
2018-04-03 23:56:48 +02:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.LOCAL,
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
2017-10-19 18:55:27 +02:00
|
|
|
this.index = index;
|
2019-02-21 00:11:22 +01:00
|
|
|
assert(type != Type.void);
|
|
|
|
this.setType(type);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A yet unresolved function prototype. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class FunctionPrototype extends DeclaredElement {
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Operator kind, if an overload. */
|
|
|
|
operatorKind: OperatorKind = OperatorKind.INVALID;
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Already resolved instances. */
|
|
|
|
instances: Map<string,Function> | null = null;
|
|
|
|
|
|
|
|
/** Clones of this prototype that are bounds to specific classes. */
|
|
|
|
private boundPrototypes: Map<Class,FunctionPrototype> | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new function prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple na,e */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file, namespace or class (if a method). */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. */
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration: FunctionDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
2018-04-11 23:35:19 +02:00
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.FUNCTION_PROTOTYPE,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, declaration.is(CommonFlags.INSTANCE)),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = decoratorFlags;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Gets the associated type parameter nodes. */
|
|
|
|
get typeParameterNodes(): TypeParameterNode[] | null {
|
|
|
|
return (<FunctionDeclaration>this.declaration).typeParameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated signature node. */
|
|
|
|
get signatureNode(): SignatureNode {
|
|
|
|
return (<FunctionDeclaration>this.declaration).signature;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated body node. */
|
|
|
|
get bodyNode(): Statement | null {
|
|
|
|
return (<FunctionDeclaration>this.declaration).body;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tests if this prototype is bound to a class. */
|
|
|
|
get isBound(): bool {
|
|
|
|
var parent = this.parent;
|
|
|
|
return parent.kind == ElementKind.CLASS
|
|
|
|
|| parent.kind == ElementKind.PROPERTY_PROTOTYPE && parent.parent.kind == ElementKind.CLASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Creates a clone of this prototype that is bound to a concrete class instead. */
|
|
|
|
toBound(classInstance: Class): FunctionPrototype {
|
|
|
|
assert(this.is(CommonFlags.INSTANCE));
|
|
|
|
assert(!this.isBound);
|
|
|
|
var boundPrototypes = this.boundPrototypes;
|
|
|
|
if (!boundPrototypes) this.boundPrototypes = boundPrototypes = new Map();
|
|
|
|
else if (boundPrototypes.has(classInstance)) return boundPrototypes.get(classInstance)!;
|
|
|
|
var declaration = this.declaration; assert(declaration.kind == NodeKind.METHODDECLARATION);
|
|
|
|
var bound = new FunctionPrototype(
|
|
|
|
this.name,
|
|
|
|
classInstance, // !
|
|
|
|
<MethodDeclaration>declaration,
|
|
|
|
this.decoratorFlags
|
|
|
|
);
|
|
|
|
bound.flags = this.flags;
|
|
|
|
bound.operatorKind = this.operatorKind;
|
|
|
|
// NOTE: this.instances holds instances per bound class / unbound
|
|
|
|
boundPrototypes.set(classInstance, bound);
|
|
|
|
return bound;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the resolved instance for the specified instance key, if already resolved. */
|
|
|
|
getResolvedInstance(instanceKey: string): Function | null {
|
|
|
|
var instances = this.instances;
|
|
|
|
if (instances && instances.has(instanceKey)) return <Function>instances.get(instanceKey);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sets the resolved instance for the specified instance key. */
|
|
|
|
setResolvedInstance(instanceKey: string, instance: Function): void {
|
|
|
|
var instances = this.instances;
|
|
|
|
if (!instances) this.instances = instances = new Map();
|
|
|
|
else assert(!instances.has(instanceKey));
|
|
|
|
instances.set(instanceKey, instance);
|
2018-12-07 14:33:32 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved function. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class Function extends TypedElement {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Function prototype. */
|
2017-12-13 04:46:05 +01:00
|
|
|
prototype: FunctionPrototype;
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Function signature. */
|
|
|
|
signature: Signature;
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Map of locals by name. */
|
2018-04-25 05:04:35 +02:00
|
|
|
localsByName: Map<string,Local> = new Map();
|
|
|
|
/** Array of locals by index. */
|
|
|
|
localsByIndex: Local[] = [];
|
2017-11-20 23:39:50 +01:00
|
|
|
/** List of additional non-parameter locals. */
|
|
|
|
additionalLocals: Type[] = [];
|
|
|
|
/** Contextual type arguments. */
|
2017-12-15 15:00:19 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null;
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Default control flow. */
|
2018-01-18 23:34:12 +01:00
|
|
|
flow: Flow;
|
2018-02-02 03:07:54 +01:00
|
|
|
/** Remembered debug locations. */
|
2018-05-06 05:46:35 +02:00
|
|
|
debugLocations: Range[] = [];
|
2018-02-28 01:48:01 +01:00
|
|
|
/** Function reference, if compiled. */
|
|
|
|
ref: FunctionRef = 0;
|
|
|
|
/** Function table index, if any. */
|
|
|
|
functionTableIndex: i32 = -1;
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Trampoline function for calling with omitted arguments. */
|
|
|
|
trampoline: Function | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Counting id of inline operations involving this function. */
|
2018-04-11 23:35:19 +02:00
|
|
|
nextInlineId: i32 = 0;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Constructs a new concrete function. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Name incl. type parameters, i.e. `foo<i32>`. */
|
|
|
|
nameInclTypeParameters: string,
|
|
|
|
/** Respective function prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
prototype: FunctionPrototype,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Concrete signature. */
|
|
|
|
signature: Signature, // pre-resolved
|
|
|
|
/** Contextual type arguments inherited from its parent class, if any. */
|
2018-04-11 23:35:19 +02:00
|
|
|
contextualTypeArguments: Map<string,Type> | null = null
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.FUNCTION,
|
|
|
|
nameInclTypeParameters,
|
|
|
|
mangleInternalName(nameInclTypeParameters, prototype.parent, prototype.is(CommonFlags.INSTANCE)),
|
|
|
|
prototype.program,
|
|
|
|
prototype.parent,
|
|
|
|
prototype.declaration
|
|
|
|
);
|
2017-12-13 04:46:05 +01:00
|
|
|
this.prototype = prototype;
|
2018-03-12 14:06:39 +01:00
|
|
|
this.signature = signature;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.flags = prototype.flags | CommonFlags.RESOLVED;
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = prototype.decoratorFlags;
|
|
|
|
this.contextualTypeArguments = contextualTypeArguments;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.type = Type.u32.asFunction(signature);
|
2019-02-06 23:42:43 +01:00
|
|
|
if (!prototype.is(CommonFlags.AMBIENT)) {
|
|
|
|
let localIndex = 0;
|
2019-02-21 00:11:22 +01:00
|
|
|
if (this.is(CommonFlags.INSTANCE)) {
|
2019-02-06 23:42:43 +01:00
|
|
|
let local = new Local(
|
2019-02-21 00:11:22 +01:00
|
|
|
CommonSymbols.this_,
|
2019-02-06 23:42:43 +01:00
|
|
|
localIndex++,
|
2019-02-21 00:11:22 +01:00
|
|
|
assert(signature.thisType),
|
|
|
|
this
|
2019-02-06 23:42:43 +01:00
|
|
|
);
|
2019-02-21 00:11:22 +01:00
|
|
|
this.localsByName.set(CommonSymbols.this_, local);
|
2019-02-06 23:42:43 +01:00
|
|
|
this.localsByIndex[local.index] = local;
|
|
|
|
}
|
|
|
|
let parameterTypes = signature.parameterTypes;
|
|
|
|
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
|
|
|
|
let parameterType = parameterTypes[i];
|
|
|
|
let parameterName = signature.getParameterName(i);
|
|
|
|
let local = new Local(
|
|
|
|
parameterName,
|
|
|
|
localIndex++,
|
2019-02-21 00:11:22 +01:00
|
|
|
parameterType,
|
|
|
|
this
|
2019-02-06 23:42:43 +01:00
|
|
|
);
|
|
|
|
this.localsByName.set(parameterName, local);
|
|
|
|
this.localsByIndex[local.index] = local;
|
2017-12-15 15:00:19 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
this.flow = Flow.create(this);
|
2019-02-21 00:11:22 +01:00
|
|
|
registerConcreteElement(this.program, this);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Adds a local of the specified type, with an optional name. */
|
2018-04-03 23:56:48 +02:00
|
|
|
addLocal(type: Type, name: string | null = null, declaration: VariableDeclaration | null = null): Local {
|
2017-10-19 18:55:27 +02:00
|
|
|
// if it has a name, check previously as this method will throw otherwise
|
2018-03-12 14:06:39 +01:00
|
|
|
var localIndex = this.signature.parameterTypes.length + this.additionalLocals.length;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.is(CommonFlags.INSTANCE)) ++localIndex;
|
2019-02-21 00:11:22 +01:00
|
|
|
var localName = name !== null
|
|
|
|
? name
|
|
|
|
: "var$" + localIndex.toString();
|
2018-02-25 00:13:39 +01:00
|
|
|
var local = new Local(
|
2019-02-21 00:11:22 +01:00
|
|
|
localName,
|
2018-02-25 00:13:39 +01:00
|
|
|
localIndex,
|
2018-04-03 23:56:48 +02:00
|
|
|
type,
|
2019-02-21 00:11:22 +01:00
|
|
|
this,
|
|
|
|
declaration || this.program.makeNativeVariableDeclaration(localName)
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2017-10-19 18:55:27 +02:00
|
|
|
if (name) {
|
2018-04-25 05:04:35 +02:00
|
|
|
if (this.localsByName.has(name)) throw new Error("duplicate local name");
|
|
|
|
this.localsByName.set(name, local);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-04-25 05:04:35 +02:00
|
|
|
this.localsByIndex[local.index] = local;
|
2017-10-19 18:55:27 +02:00
|
|
|
this.additionalLocals.push(type);
|
|
|
|
return local;
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
var locals = this.localsByName;
|
|
|
|
if (locals.has(name)) return locals.get(name);
|
|
|
|
return this.parent.lookup(name);
|
|
|
|
}
|
|
|
|
|
2019-02-06 23:42:43 +01:00
|
|
|
// used by flows to keep track of temporary locals
|
|
|
|
tempI32s: Local[] | null = null;
|
|
|
|
tempI64s: Local[] | null = null;
|
|
|
|
tempF32s: Local[] | null = null;
|
|
|
|
tempF64s: Local[] | null = null;
|
2017-12-12 09:32:03 +01:00
|
|
|
|
2019-02-06 23:42:43 +01:00
|
|
|
// used by flows to keep track of break labels
|
|
|
|
nextBreakId: i32 = 0;
|
|
|
|
breakStack: i32[] | null = null;
|
|
|
|
breakLabel: string | null = null;
|
2017-12-15 15:00:19 +01:00
|
|
|
|
|
|
|
/** Finalizes the function once compiled, releasing no longer needed resources. */
|
2018-02-02 03:07:54 +01:00
|
|
|
finalize(module: Module, ref: FunctionRef): void {
|
2018-02-28 01:48:01 +01:00
|
|
|
this.ref = ref;
|
2018-01-02 03:54:06 +01:00
|
|
|
assert(!this.breakStack || !this.breakStack.length); // internal error
|
2017-12-15 15:00:19 +01:00
|
|
|
this.breakStack = null;
|
2019-02-06 23:42:43 +01:00
|
|
|
this.breakLabel = null;
|
2017-12-15 15:00:19 +01:00
|
|
|
this.tempI32s = this.tempI64s = this.tempF32s = this.tempF64s = null;
|
2018-02-02 03:07:54 +01:00
|
|
|
if (this.program.options.sourceMap) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let debugLocations = this.debugLocations;
|
2018-05-06 05:46:35 +02:00
|
|
|
for (let i = 0, k = debugLocations.length; i < k; ++i) {
|
|
|
|
let debugLocation = debugLocations[i];
|
|
|
|
module.setDebugLocation(
|
|
|
|
ref,
|
|
|
|
debugLocation.debugInfoRef,
|
|
|
|
debugLocation.source.debugInfoIndex,
|
|
|
|
debugLocation.line,
|
|
|
|
debugLocation.column
|
|
|
|
);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-02-02 03:07:54 +01:00
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2018-03-13 02:32:10 +01:00
|
|
|
/** A resolved function target, that is a function called indirectly by an index and signature. */
|
2018-03-12 22:34:40 +01:00
|
|
|
export class FunctionTarget extends Element {
|
|
|
|
|
|
|
|
/** Underlying signature. */
|
|
|
|
signature: Signature;
|
|
|
|
/** Function type. */
|
|
|
|
type: Type;
|
|
|
|
|
|
|
|
/** Constructs a new function target. */
|
2019-02-21 00:11:22 +01:00
|
|
|
constructor(
|
|
|
|
/** Concrete signature. */
|
|
|
|
signature: Signature,
|
|
|
|
/** Program reference. */
|
|
|
|
program: Program,
|
|
|
|
__s: string = "" // FIXME: current TS limitation workaround, but a fix seems underway
|
|
|
|
) {
|
|
|
|
super(
|
|
|
|
ElementKind.FUNCTION_TARGET,
|
|
|
|
__s = "sig:" + signature.toSignatureString(),
|
|
|
|
__s,
|
|
|
|
program,
|
|
|
|
program.nativeFile
|
|
|
|
);
|
2018-03-12 22:34:40 +01:00
|
|
|
this.signature = signature;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.flags = CommonFlags.RESOLVED;
|
2018-03-12 22:34:40 +01:00
|
|
|
this.type = Type.u32.asFunction(signature);
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return null;
|
|
|
|
}
|
2018-03-12 22:34:40 +01:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A yet unresolved instance field prototype. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class FieldPrototype extends DeclaredElement {
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new field prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype,
|
|
|
|
/** Declaration reference. */
|
|
|
|
declaration: FieldDeclaration,
|
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.FIELD_PROTOTYPE,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, assert(declaration.is(CommonFlags.INSTANCE))),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
|
|
|
this.decoratorFlags = decoratorFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated type node. */
|
|
|
|
get typeNode(): CommonTypeNode | null {
|
|
|
|
return (<FieldDeclaration>this.declaration).type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated initializer node. */
|
|
|
|
get initializerNode(): Expression | null {
|
|
|
|
return (<FieldDeclaration>this.declaration).initializer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the associated parameter index. Set if declared as a constructor parameter, otherwise `-1`. */
|
|
|
|
get parameterIndex(): i32 {
|
|
|
|
return (<FieldDeclaration>this.declaration).parameterIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-11-17 14:33:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** A resolved instance field. */
|
2018-01-17 02:08:14 +01:00
|
|
|
export class Field extends VariableLikeElement {
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Field prototype reference. */
|
|
|
|
prototype: FieldPrototype;
|
2018-01-01 20:27:21 +01:00
|
|
|
/** Field memory offset, if an instance field. */
|
|
|
|
memoryOffset: i32 = -1;
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new field. */
|
2018-04-03 23:56:48 +02:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Respective field prototype. */
|
2018-04-03 23:56:48 +02:00
|
|
|
prototype: FieldPrototype,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Parent class. */
|
|
|
|
parent: Class,
|
|
|
|
/** Concrete type. */
|
|
|
|
type: Type
|
2018-04-03 23:56:48 +02:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.FIELD,
|
|
|
|
prototype.name,
|
|
|
|
parent,
|
|
|
|
<VariableLikeDeclarationStatement>prototype.declaration
|
|
|
|
);
|
2018-01-03 18:33:27 +01:00
|
|
|
this.prototype = prototype;
|
2017-12-13 04:46:05 +01:00
|
|
|
this.flags = prototype.flags;
|
2019-02-21 00:11:22 +01:00
|
|
|
assert(type != Type.void);
|
|
|
|
this.setType(type);
|
|
|
|
registerConcreteElement(this.program, this);
|
2017-11-17 14:33:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 02:37:53 +01:00
|
|
|
/** A property comprised of a getter and a setter function. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class PropertyPrototype extends DeclaredElement {
|
2017-12-16 17:54:53 +01:00
|
|
|
|
2017-12-27 02:37:53 +01:00
|
|
|
/** Getter prototype. */
|
|
|
|
getterPrototype: FunctionPrototype | null = null;
|
|
|
|
/** Setter prototype. */
|
|
|
|
setterPrototype: FunctionPrototype | null = null;
|
|
|
|
|
|
|
|
/** Constructs a new property prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent class. */
|
|
|
|
parent: ClassPrototype,
|
|
|
|
/** Declaration of the getter or setter introducing the property. */
|
|
|
|
firstDeclaration: FunctionDeclaration
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
ElementKind.PROPERTY_PROTOTYPE,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, firstDeclaration.is(CommonFlags.INSTANCE)),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
firstDeclaration
|
|
|
|
);
|
|
|
|
this.flags &= ~(CommonFlags.GET | CommonFlags.SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2017-12-16 17:54:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** A resolved property. */
|
|
|
|
export class Property extends VariableLikeElement {
|
|
|
|
|
|
|
|
/** Prototype reference. */
|
|
|
|
prototype: PropertyPrototype;
|
|
|
|
/** Getter instance. */
|
|
|
|
getterInstance: Function | null = null;
|
|
|
|
/** Setter instance. */
|
|
|
|
setterInstance: Function | null = null;
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Constructs a new property prototype. */
|
|
|
|
constructor(
|
|
|
|
/** Respective property prototype. */
|
|
|
|
prototype: PropertyPrototype,
|
|
|
|
/** Parent element, usually a static class prototype or class instance. */
|
|
|
|
parent: Element
|
|
|
|
) {
|
|
|
|
super(
|
|
|
|
ElementKind.PROPERTY,
|
|
|
|
prototype.name,
|
|
|
|
parent,
|
|
|
|
prototype.program.makeNativeVariableDeclaration(
|
|
|
|
prototype.name,
|
|
|
|
prototype.is(CommonFlags.INSTANCE)
|
|
|
|
? CommonFlags.INSTANCE
|
|
|
|
: CommonFlags.NONE
|
|
|
|
)
|
|
|
|
);
|
|
|
|
this.prototype = prototype;
|
|
|
|
registerConcreteElement(this.program, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** A yet unresolved class prototype. */
|
|
|
|
export class ClassPrototype extends DeclaredElement {
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2017-12-15 02:50:55 +01:00
|
|
|
/** Instance member prototypes. */
|
|
|
|
instanceMembers: Map<string,Element> | null = null;
|
2018-01-07 15:07:46 +01:00
|
|
|
/** Base class prototype, if applicable. */
|
|
|
|
basePrototype: ClassPrototype | null = null; // set in Program#initialize
|
2018-01-27 05:35:14 +01:00
|
|
|
/** Constructor prototype. */
|
|
|
|
constructorPrototype: FunctionPrototype | null = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Operator overload prototypes. */
|
|
|
|
overloadPrototypes: Map<OperatorKind, FunctionPrototype> = new Map();
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Already resolved instances. */
|
|
|
|
instances: Map<string,Class> | null = null;
|
2018-01-06 10:20:38 +01:00
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Simple name. */
|
|
|
|
name: string,
|
|
|
|
/** Parent element, usually a file or namespace. */
|
|
|
|
parent: Element,
|
|
|
|
/** Declaration reference. */
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration: ClassDeclaration,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Pre-checked flags indicating built-in decorators. */
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE,
|
|
|
|
_isInterface: bool = false // FIXME
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
_isInterface ? ElementKind.INTERFACE_PROTOTYPE : ElementKind.CLASS_PROTOTYPE,
|
|
|
|
name,
|
|
|
|
mangleInternalName(name, parent, declaration.is(CommonFlags.INSTANCE)),
|
|
|
|
parent.program,
|
|
|
|
parent,
|
|
|
|
declaration
|
|
|
|
);
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = decoratorFlags;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-04 01:36:26 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Gets the associated type parameter nodes. */
|
|
|
|
get typeParameterNodes(): TypeParameterNode[] | null {
|
|
|
|
return (<ClassDeclaration>this.declaration).typeParameters;
|
|
|
|
}
|
|
|
|
/** Gets the associated extends node. */
|
|
|
|
get extendsNode(): TypeNode | null {
|
|
|
|
return (<ClassDeclaration>this.declaration).extendsType;
|
|
|
|
}
|
|
|
|
/** Gets the associated implements nodes. */
|
|
|
|
get implementsNodes(): TypeNode[] | null {
|
|
|
|
return (<ClassDeclaration>this.declaration).implementsTypes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tests if this prototype extends the specified. */
|
2019-02-03 11:41:04 +02:00
|
|
|
extends(basePtototype: ClassPrototype | null): bool {
|
|
|
|
var current: ClassPrototype | null = this;
|
|
|
|
do {
|
|
|
|
if (current === basePtototype) return true;
|
|
|
|
} while (current = current.basePrototype);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Adds an element as an instance member of this one. Returns the previous element if a duplicate. */
|
|
|
|
addInstance(name: string, element: DeclaredElement): bool {
|
|
|
|
var originalDeclaration = element.declaration;
|
|
|
|
var instanceMembers = this.instanceMembers;
|
|
|
|
if (!instanceMembers) this.instanceMembers = instanceMembers = new Map();
|
|
|
|
else if (instanceMembers.has(name)) {
|
|
|
|
let merged = tryMerge(instanceMembers.get(name)!, element);
|
|
|
|
if (!merged) {
|
|
|
|
this.program.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
element.identifierNode.range, element.identifierNode.text
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
element = merged;
|
|
|
|
}
|
|
|
|
instanceMembers.set(name, element);
|
|
|
|
if (element.is(CommonFlags.EXPORT) && this.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
element.set(CommonFlags.MODULE_EXPORT); // propagate
|
|
|
|
}
|
|
|
|
this.program.elementsByDeclaration.set(originalDeclaration, element);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the resolved instance for the specified instance key, if already resolved. */
|
|
|
|
getResolvedInstance(instanceKey: string): Class | null {
|
|
|
|
var instances = this.instances;
|
|
|
|
if (instances && instances.has(instanceKey)) return <Class>instances.get(instanceKey);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sets the resolved instance for the specified instance key. */
|
|
|
|
setResolvedInstance(instanceKey: string, instance: Class): void {
|
|
|
|
var instances = this.instances;
|
|
|
|
if (!instances) this.instances = instances = new Map();
|
|
|
|
else assert(!instances.has(instanceKey));
|
|
|
|
instances.set(instanceKey, instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved class. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class Class extends TypedElement {
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Class prototype. */
|
2017-12-13 04:46:05 +01:00
|
|
|
prototype: ClassPrototype;
|
|
|
|
/** Resolved type arguments. */
|
2018-01-01 20:27:21 +01:00
|
|
|
typeArguments: Type[] | null;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Base class, if applicable. */
|
|
|
|
base: Class | null;
|
2017-12-15 15:00:19 +01:00
|
|
|
/** Contextual type arguments for fields and methods. */
|
|
|
|
contextualTypeArguments: Map<string,Type> | null = null;
|
2018-01-13 23:38:07 +01:00
|
|
|
/** Current member memory offset. */
|
|
|
|
currentMemoryOffset: u32 = 0;
|
2018-01-28 15:13:31 +01:00
|
|
|
/** Constructor instance. */
|
|
|
|
constructorInstance: Function | null = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Operator overloads. */
|
|
|
|
overloads: Map<OperatorKind,Function> | null = null;
|
2018-08-02 18:23:02 +02:00
|
|
|
/** Function index of the GC hook. */
|
|
|
|
gcHookIndex: u32 = <u32>-1;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new class. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Name incl. type parameters, i.e. `Foo<i32>`. */
|
|
|
|
nameInclTypeParameters: string,
|
|
|
|
/** The respective class prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
prototype: ClassPrototype,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Concrete type arguments, if any. */
|
2018-02-25 00:13:39 +01:00
|
|
|
typeArguments: Type[] | null = null,
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Base class, if derived. */
|
|
|
|
base: Class | null = null,
|
|
|
|
_isInterface: bool = false // FIXME
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
_isInterface ? ElementKind.INTERFACE : ElementKind.CLASS,
|
|
|
|
nameInclTypeParameters,
|
|
|
|
mangleInternalName(nameInclTypeParameters, prototype.parent, prototype.is(CommonFlags.INSTANCE)),
|
|
|
|
prototype.program,
|
|
|
|
prototype.parent,
|
|
|
|
prototype.declaration
|
|
|
|
);
|
2017-12-13 04:46:05 +01:00
|
|
|
this.prototype = prototype;
|
|
|
|
this.flags = prototype.flags;
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = prototype.decoratorFlags;
|
2017-10-19 18:55:27 +02:00
|
|
|
this.typeArguments = typeArguments;
|
2019-02-21 00:11:22 +01:00
|
|
|
this.setType(this.program.options.usizeType.asClass(this));
|
2017-12-13 23:24:13 +01:00
|
|
|
this.base = base;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-01-05 18:19:32 +01:00
|
|
|
// inherit static members and contextual type arguments from base class
|
|
|
|
if (base) {
|
2018-04-11 23:35:19 +02:00
|
|
|
let inheritedTypeArguments = base.contextualTypeArguments;
|
|
|
|
if (inheritedTypeArguments) {
|
2019-02-21 00:11:22 +01:00
|
|
|
let contextualTypeArguments = this.contextualTypeArguments;
|
2018-04-11 23:35:19 +02:00
|
|
|
for (let [baseName, baseType] of inheritedTypeArguments) {
|
2019-02-21 00:11:22 +01:00
|
|
|
if (!contextualTypeArguments) this.contextualTypeArguments = contextualTypeArguments = new Map();
|
|
|
|
contextualTypeArguments.set(baseName, baseType);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-05 18:19:32 +01:00
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
// apply pre-checked instance-specific contextual type arguments
|
|
|
|
var typeParameters = prototype.typeParameterNodes;
|
|
|
|
if (typeArguments) {
|
|
|
|
let numTypeArguments = typeArguments.length;
|
|
|
|
if (!typeParameters || numTypeArguments != typeParameters.length) {
|
2018-01-02 03:54:06 +01:00
|
|
|
throw new Error("type argument count mismatch");
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
if (numTypeArguments) {
|
|
|
|
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
|
|
|
|
for (let i = 0; i < numTypeArguments; ++i) {
|
|
|
|
this.contextualTypeArguments.set(typeParameters[i].name.text, typeArguments[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (typeParameters && typeParameters.length) {
|
|
|
|
throw new Error("type argument count mismatch");
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
registerConcreteElement(this.program, this);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-01 02:08:03 +01:00
|
|
|
|
2018-02-28 01:48:01 +01:00
|
|
|
/** Tests if a value of this class type is assignable to a target of the specified class type. */
|
|
|
|
isAssignableTo(target: Class): bool {
|
|
|
|
var current: Class | null = this;
|
2018-04-11 23:35:19 +02:00
|
|
|
do if (current == target) return true;
|
|
|
|
while (current = current.base);
|
2018-02-28 01:48:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Looks up the operator overload of the specified kind. */
|
2018-04-25 05:04:35 +02:00
|
|
|
lookupOverload(kind: OperatorKind, unchecked: bool = false): Function | null {
|
|
|
|
if (unchecked) {
|
|
|
|
switch (kind) {
|
|
|
|
case OperatorKind.INDEXED_GET: {
|
|
|
|
let uncheckedOverload = this.lookupOverload(OperatorKind.UNCHECKED_INDEXED_GET);
|
|
|
|
if (uncheckedOverload) return uncheckedOverload;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OperatorKind.INDEXED_SET: {
|
|
|
|
let uncheckedOverload = this.lookupOverload(OperatorKind.UNCHECKED_INDEXED_SET);
|
|
|
|
if (uncheckedOverload) return uncheckedOverload;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: assert(false);
|
|
|
|
}
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
var instance: Class | null = this;
|
|
|
|
do {
|
|
|
|
let overloads = instance.overloads;
|
|
|
|
if (overloads) {
|
|
|
|
let overload = overloads.get(kind);
|
|
|
|
if (overload) return overload;
|
|
|
|
}
|
|
|
|
} while (instance = instance.base);
|
|
|
|
return null;
|
2018-04-05 02:23:03 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/* @override */
|
|
|
|
lookup(name: string): Element | null {
|
|
|
|
return this.parent.lookup(name);
|
2019-02-03 11:41:04 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Calculates the memory offset of the specified field. */
|
2018-08-04 00:36:59 +02:00
|
|
|
offsetof(fieldName: string): u32 {
|
|
|
|
var members = assert(this.members);
|
|
|
|
assert(members.has(fieldName));
|
|
|
|
var field = <Element>members.get(fieldName);
|
|
|
|
assert(field.kind == ElementKind.FIELD);
|
|
|
|
return (<Field>field).memoryOffset;
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
/** A yet unresolved interface. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class InterfacePrototype extends ClassPrototype { // FIXME
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new interface prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
name: string,
|
|
|
|
parent: Element,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration: InterfaceDeclaration,
|
|
|
|
decoratorFlags: DecoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
name,
|
|
|
|
parent,
|
|
|
|
declaration,
|
|
|
|
decoratorFlags,
|
|
|
|
true
|
|
|
|
);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved interface. */
|
2019-02-21 00:11:22 +01:00
|
|
|
export class Interface extends Class { // FIXME
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new interface. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
2019-02-21 00:11:22 +01:00
|
|
|
nameInclTypeParameters: string,
|
2018-02-25 00:13:39 +01:00
|
|
|
prototype: InterfacePrototype,
|
|
|
|
typeArguments: Type[] = [],
|
|
|
|
base: Interface | null = null
|
|
|
|
) {
|
2019-02-21 00:11:22 +01:00
|
|
|
super(
|
|
|
|
nameInclTypeParameters,
|
|
|
|
prototype,
|
|
|
|
typeArguments,
|
|
|
|
base,
|
|
|
|
true
|
|
|
|
);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Registers a concrete element with a program. */
|
|
|
|
function registerConcreteElement(program: Program, element: Element): void {
|
|
|
|
assert(!program.instancesByName.has(element.internalName));
|
|
|
|
program.instancesByName.set(element.internalName, element);
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Attempts to merge two elements. Returns the merged element on success. */
|
|
|
|
function tryMerge(older: Element, newer: Element): DeclaredElement | null {
|
|
|
|
// NOTE: some of the following cases are not supported by TS, not sure why exactly.
|
|
|
|
// suggesting to just merge what seems to be possible for now and revisit later.
|
|
|
|
assert(older.program === newer.program);
|
|
|
|
assert(!newer.members);
|
|
|
|
var merged: DeclaredElement | null = null;
|
|
|
|
switch (older.kind) {
|
|
|
|
case ElementKind.FUNCTION_PROTOTYPE: {
|
|
|
|
switch (newer.kind) {
|
|
|
|
case ElementKind.NAMESPACE: {
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.TYPEDEFINITION: {
|
|
|
|
if (!older.shadowType) {
|
|
|
|
older.shadowType = <TypeDefinition>newer;
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case ElementKind.CLASS_PROTOTYPE:
|
|
|
|
case ElementKind.ENUM: {
|
|
|
|
if (newer.kind == ElementKind.NAMESPACE) {
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
2019-02-06 23:42:43 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case ElementKind.NAMESPACE: {
|
|
|
|
switch (newer.kind) {
|
|
|
|
case ElementKind.ENUM:
|
|
|
|
case ElementKind.CLASS_PROTOTYPE: // TS2434
|
|
|
|
case ElementKind.FUNCTION_PROTOTYPE: { // TS2434
|
|
|
|
copyMembers(older, newer);
|
|
|
|
merged = <DeclaredElement>newer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.NAMESPACE: {
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.TYPEDEFINITION: {
|
|
|
|
if (!older.shadowType) {
|
|
|
|
older.shadowType = <TypeDefinition>newer;
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
|
|
|
}
|
|
|
|
break;
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case ElementKind.GLOBAL: {
|
|
|
|
if (newer.kind == ElementKind.TYPEDEFINITION) {
|
|
|
|
if (!older.shadowType) {
|
|
|
|
older.shadowType = <TypeDefinition>newer;
|
|
|
|
copyMembers(newer, older);
|
|
|
|
merged = <DeclaredElement>older;
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
2018-04-25 05:04:35 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2018-04-25 05:04:35 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case ElementKind.TYPEDEFINITION: {
|
|
|
|
switch (newer.kind) {
|
|
|
|
case ElementKind.GLOBAL:
|
|
|
|
case ElementKind.FUNCTION_PROTOTYPE:
|
|
|
|
case ElementKind.NAMESPACE: {
|
|
|
|
if (!newer.shadowType) {
|
|
|
|
newer.shadowType = <TypeDefinition>older;
|
|
|
|
copyMembers(older, newer);
|
|
|
|
merged = <DeclaredElement>newer;
|
|
|
|
}
|
|
|
|
break;
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
break;
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
if (merged) {
|
|
|
|
if (older.is(CommonFlags.EXPORT) != newer.is(CommonFlags.EXPORT)) {
|
|
|
|
older.program.error(
|
|
|
|
DiagnosticCode.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local,
|
|
|
|
merged.identifierNode.range, merged.identifierNode.text
|
|
|
|
);
|
2019-02-07 11:40:23 +01:00
|
|
|
}
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
return merged;
|
|
|
|
}
|
2019-02-06 23:42:43 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Copies the members of `src` to `dest`. */
|
|
|
|
function copyMembers(src: Element, dest: Element): void {
|
|
|
|
var srcMembers = src.members;
|
|
|
|
if (srcMembers) {
|
|
|
|
let destMembers = dest.members;
|
|
|
|
if (!destMembers) dest.members = destMembers = new Map();
|
|
|
|
for (let [memberName, member] of srcMembers) {
|
|
|
|
destMembers.set(memberName, member);
|
2019-02-06 23:42:43 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
}
|
2019-02-06 23:42:43 +01:00
|
|
|
|
2019-02-21 00:11:22 +01:00
|
|
|
/** Mangles the internal name of an element with the specified name that is a child of the given parent. */
|
|
|
|
export function mangleInternalName(name: string, parent: Element, isInstance: bool, asGlobal: bool = false): string {
|
|
|
|
switch (parent.kind) {
|
|
|
|
case ElementKind.FILE: {
|
|
|
|
if (asGlobal) return name;
|
|
|
|
return parent.internalName + PATH_DELIMITER + name;
|
2018-06-27 02:53:45 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
case ElementKind.FUNCTION: {
|
|
|
|
if (asGlobal) return name;
|
|
|
|
assert(!isInstance);
|
|
|
|
return parent.internalName + INNER_DELIMITER + name;
|
2018-05-06 00:00:54 +02:00
|
|
|
}
|
2019-02-21 00:11:22 +01:00
|
|
|
default: {
|
|
|
|
return mangleInternalName(parent.name, parent.parent, parent.is(CommonFlags.INSTANCE), asGlobal)
|
|
|
|
+ (isInstance ? INSTANCE_DELIMITER : STATIC_DELIMITER) + name;
|
2018-05-06 00:00:54 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|