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
|
|
|
|
2017-12-24 03:19:47 +01:00
|
|
|
import {
|
2018-01-21 17:52:44 +01:00
|
|
|
Options
|
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-03-12 14:06:39 +01:00
|
|
|
Signature,
|
|
|
|
|
2017-12-24 03:19:47 +01:00
|
|
|
typesToString
|
|
|
|
} from "./types";
|
|
|
|
|
|
|
|
import {
|
2017-09-28 13:08:25 +02:00
|
|
|
Node,
|
|
|
|
NodeKind,
|
2017-10-02 12:52:15 +02:00
|
|
|
Source,
|
2017-10-19 18:55:27 +02:00
|
|
|
Range,
|
2018-03-12 14:06:39 +01:00
|
|
|
CommonTypeNode,
|
2017-10-19 18:55:27 +02:00
|
|
|
TypeNode,
|
2018-03-12 14:06:39 +01:00
|
|
|
TypeParameterNode,
|
2018-04-15 00:34:19 +02:00
|
|
|
ParameterKind,
|
|
|
|
SignatureNode,
|
2018-03-12 14:06:39 +01:00
|
|
|
DecoratorNode,
|
2018-01-06 10:20:38 +01:00
|
|
|
DecoratorKind,
|
2017-12-24 03:19:47 +01:00
|
|
|
|
2017-10-19 18:55:27 +02:00
|
|
|
Expression,
|
2018-01-29 22:36:07 +01:00
|
|
|
AssertionExpression,
|
2018-01-06 10:20:38 +01:00
|
|
|
ElementAccessExpression,
|
2017-10-19 18:55:27 +02:00
|
|
|
IdentifierExpression,
|
2018-01-06 10:20:38 +01:00
|
|
|
LiteralExpression,
|
|
|
|
LiteralKind,
|
2018-01-29 22:36:07 +01:00
|
|
|
ParenthesizedExpression,
|
2017-10-19 18:55:27 +02:00
|
|
|
PropertyAccessExpression,
|
|
|
|
StringLiteralExpression,
|
2017-12-16 02:27:39 +01:00
|
|
|
CallExpression,
|
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-06-01 14:17:27 +02:00
|
|
|
decoratorNameToKind
|
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,
|
|
|
|
NativeType,
|
|
|
|
FunctionRef,
|
2018-05-06 00:00:54 +02:00
|
|
|
ExpressionRef,
|
|
|
|
ExpressionId,
|
|
|
|
BinaryOp,
|
|
|
|
UnaryOp,
|
|
|
|
|
|
|
|
getExpressionId,
|
|
|
|
getGetLocalIndex,
|
|
|
|
isTeeLocal,
|
|
|
|
getSetLocalValue,
|
|
|
|
getBinaryOp,
|
|
|
|
getConstValueI32,
|
|
|
|
getBinaryLeft,
|
|
|
|
getBinaryRight,
|
|
|
|
getUnaryOp,
|
|
|
|
getExpressionType,
|
|
|
|
getLoadBytes,
|
|
|
|
isLoadSigned,
|
|
|
|
getIfTrue,
|
|
|
|
getIfFalse,
|
|
|
|
getSelectThen,
|
|
|
|
getSelectElse,
|
|
|
|
getCallTarget,
|
|
|
|
getBlockChildCount,
|
|
|
|
getBlockChild,
|
|
|
|
getBlockName,
|
|
|
|
getConstValueF32,
|
|
|
|
getConstValueF64,
|
|
|
|
getConstValueI64Low
|
2017-12-24 03:19:47 +01:00
|
|
|
} from "./module";
|
2018-05-30 16:22:56 +02:00
|
|
|
import { CharCode } from "./util";
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2018-01-19 16:13:14 +01:00
|
|
|
/** Path delimiter inserted between file system levels. */
|
|
|
|
export const PATH_DELIMITER = "/";
|
|
|
|
/** Substitution used to indicate the parent directory. */
|
|
|
|
export const PARENT_SUBST = "..";
|
|
|
|
/** Function name prefix used for getters. */
|
|
|
|
export const GETTER_PREFIX = "get:";
|
|
|
|
/** Function name prefix used for setters. */
|
|
|
|
export const SETTER_PREFIX = "set:";
|
|
|
|
/** Delimiter used between class names and instance members. */
|
|
|
|
export const INSTANCE_DELIMITER = "#";
|
|
|
|
/** Delimiter used between class and namespace names and static members. */
|
|
|
|
export const STATIC_DELIMITER = ".";
|
2018-04-07 03:27:22 +02:00
|
|
|
/** Delimiter used between a function and its inner elements. */
|
|
|
|
export const INNER_DELIMITER = "~";
|
2018-02-02 03:07:54 +01:00
|
|
|
/** Substitution used to indicate a library directory. */
|
2018-04-02 19:05:26 +02:00
|
|
|
export const LIBRARY_SUBST = "~lib";
|
2018-02-02 03:07:54 +01:00
|
|
|
/** Library directory prefix. */
|
|
|
|
export const LIBRARY_PREFIX = LIBRARY_SUBST + PATH_DELIMITER;
|
2018-01-19 16:13:14 +01:00
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Represents a yet unresolved export. */
|
2017-09-29 17:25:02 +02:00
|
|
|
class QueuedExport {
|
2017-12-02 01:14:15 +01:00
|
|
|
isReExport: bool;
|
2017-10-11 17:03:22 +02:00
|
|
|
referencedName: string;
|
2017-09-29 17:25:02 +02:00
|
|
|
member: ExportMember;
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Represents a yet unresolved import. */
|
2017-09-29 17:25:02 +02:00
|
|
|
class QueuedImport {
|
2017-10-02 12:52:15 +02:00
|
|
|
internalName: string;
|
2017-12-02 01:14:15 +01:00
|
|
|
referencedName: string;
|
2018-02-16 11:55:13 +01:00
|
|
|
referencedNameAlt: string;
|
2017-09-29 17:25:02 +02:00
|
|
|
declaration: ImportDeclaration;
|
|
|
|
}
|
|
|
|
|
2018-03-12 17:44:09 +01:00
|
|
|
/** Represents a type alias. */
|
|
|
|
class TypeAlias {
|
|
|
|
typeParameters: TypeParameterNode[] | null;
|
|
|
|
type: CommonTypeNode;
|
|
|
|
}
|
|
|
|
|
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-28 04:09:40 +01:00
|
|
|
const noTypesYet = new Map<string,Type>();
|
2017-10-07 14:29:43 +02:00
|
|
|
|
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 {
|
|
|
|
|
2017-10-19 18:55:27 +02:00
|
|
|
/** Array of source files. */
|
2017-09-28 13:08:25 +02:00
|
|
|
sources: Source[];
|
2018-02-25 00:13:39 +01:00
|
|
|
/** Diagnostic offset used where repeatedly 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;
|
2017-10-19 18:55:27 +02:00
|
|
|
/** Elements by internal name. */
|
2018-03-17 01:37:05 +01:00
|
|
|
elementsLookup: Map<string,Element> = new Map();
|
2018-05-06 00:00:54 +02:00
|
|
|
/** Class and function instances by internal name. */
|
|
|
|
instancesLookup: Map<string,Element> = new Map();
|
2017-10-19 18:55:27 +02:00
|
|
|
/** Types by internal name. */
|
2018-03-17 01:37:05 +01:00
|
|
|
typesLookup: Map<string,Type> = noTypesYet;
|
2017-12-20 13:36:39 +01:00
|
|
|
/** Declared type aliases. */
|
2018-03-12 17:44:09 +01:00
|
|
|
typeAliases: Map<string,TypeAlias> = new Map();
|
2018-03-17 01:37:05 +01:00
|
|
|
/** File-level exports by exported name. */
|
|
|
|
fileLevelExports: Map<string,Element> = new Map();
|
|
|
|
/** Module-level exports by exported name. */
|
|
|
|
moduleLevelExports: Map<string,Element> = new Map();
|
2018-03-31 18:18:55 +02:00
|
|
|
/** Array prototype reference. */
|
|
|
|
arrayPrototype: ClassPrototype | null = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** ArrayBufferView prototype reference. */
|
|
|
|
arrayBufferViewPrototype: InterfacePrototype | null = null;
|
2018-03-31 18:18:55 +02:00
|
|
|
/** String instance reference. */
|
|
|
|
stringInstance: Class | null = null;
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2018-04-05 02:23:03 +02:00
|
|
|
/** Target expression of the previously resolved property or element access. */
|
|
|
|
resolvedThisExpression: Expression | null = null;
|
|
|
|
/** Element expression of the previously resolved element access. */
|
|
|
|
resolvedElementExpression : Expression | null = null;
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new program, optionally inheriting parser diagnostics. */
|
2017-09-28 13:08:25 +02:00
|
|
|
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
|
|
|
super(diagnostics);
|
2017-12-27 02:37:53 +01:00
|
|
|
this.sources = [];
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-04-07 03:27:22 +02:00
|
|
|
/** Gets a source by its exact path. */
|
|
|
|
getSource(normalizedPath: string): Source | null {
|
|
|
|
var sources = this.sources;
|
|
|
|
for (let i = 0, k = sources.length; i < k; ++i) {
|
|
|
|
let source = sources[i];
|
|
|
|
if (source.normalizedPath == normalizedPath) return source;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Looks up the source for the specified possibly ambiguous path. */
|
|
|
|
lookupSourceByPath(normalizedPathWithoutExtension: string): Source | null {
|
|
|
|
return (
|
|
|
|
this.getSource(normalizedPathWithoutExtension + ".ts") ||
|
|
|
|
this.getSource(normalizedPathWithoutExtension + "/index.ts") ||
|
|
|
|
this.getSource(LIBRARY_PREFIX + normalizedPathWithoutExtension + ".ts") ||
|
|
|
|
this.getSource(LIBRARY_PREFIX + normalizedPathWithoutExtension + "/index.ts")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
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-03-17 01:37:05 +01:00
|
|
|
this.typesLookup = new Map([
|
2017-12-05 01:45:15 +01:00
|
|
|
["i8", Type.i8],
|
|
|
|
["i16", Type.i16],
|
|
|
|
["i32", Type.i32],
|
|
|
|
["i64", Type.i64],
|
2018-01-21 17:52:44 +01:00
|
|
|
["isize", options.isizeType],
|
2017-12-05 01:45:15 +01:00
|
|
|
["u8", Type.u8],
|
|
|
|
["u16", Type.u16],
|
|
|
|
["u32", Type.u32],
|
|
|
|
["u64", Type.u64],
|
2018-01-21 17:52:44 +01:00
|
|
|
["usize", options.usizeType],
|
2017-12-05 01:45:15 +01:00
|
|
|
["bool", Type.bool],
|
|
|
|
["f32", Type.f32],
|
|
|
|
["f64", Type.f64],
|
2017-12-16 02:27:39 +01:00
|
|
|
["void", Type.void],
|
|
|
|
["number", Type.f64],
|
|
|
|
["boolean", Type.bool]
|
2017-12-05 01:45:15 +01:00
|
|
|
]);
|
2017-12-01 02:08:03 +01:00
|
|
|
|
2017-12-28 04:09:40 +01:00
|
|
|
var queuedExports = new Map<string,QueuedExport>();
|
|
|
|
var queuedImports = new Array<QueuedImport>();
|
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
|
|
|
|
2017-10-11 17:03:22 +02:00
|
|
|
// build initial lookup maps of internal names to declarations
|
2018-03-12 14:06:39 +01:00
|
|
|
for (let i = 0, k = this.sources.length; i < k; ++i) {
|
|
|
|
let source = this.sources[i];
|
|
|
|
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) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.CLASSDECLARATION: {
|
2018-04-11 23:35:19 +02:00
|
|
|
this.initializeClass(<ClassDeclaration>statement, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.ENUMDECLARATION: {
|
2017-09-28 13:08:25 +02:00
|
|
|
this.initializeEnum(<EnumDeclaration>statement);
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.EXPORT: {
|
|
|
|
this.initializeExports(<ExportStatement>statement, queuedExports);
|
2017-09-29 17:25:02 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.FUNCTIONDECLARATION: {
|
2017-09-28 13:08:25 +02:00
|
|
|
this.initializeFunction(<FunctionDeclaration>statement);
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.IMPORT: {
|
|
|
|
this.initializeImports(<ImportStatement>statement, queuedExports, queuedImports);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.INTERFACEDECLARATION: {
|
2017-09-28 13:08:25 +02:00
|
|
|
this.initializeInterface(<InterfaceDeclaration>statement);
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.NAMESPACEDECLARATION: {
|
2018-04-11 23:35:19 +02:00
|
|
|
this.initializeNamespace(<NamespaceDeclaration>statement, queuedExtends, queuedImplements);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.TYPEDECLARATION: {
|
2018-01-01 20:27:21 +01:00
|
|
|
this.initializeTypeAlias(<TypeDeclaration>statement);
|
2017-12-19 17:49:15 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.VARIABLE: {
|
2017-09-28 13:08:25 +02:00
|
|
|
this.initializeVariables(<VariableStatement>statement);
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
// queued imports should be resolvable now through traversing exports and queued exports
|
|
|
|
for (let i = 0; i < queuedImports.length;) {
|
|
|
|
let queuedImport = queuedImports[i];
|
|
|
|
let element = this.tryResolveImport(queuedImport.referencedName, queuedExports);
|
2017-12-02 01:14:15 +01:00
|
|
|
if (element) {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(queuedImport.internalName, element);
|
2017-12-28 04:09:40 +01:00
|
|
|
queuedImports.splice(i, 1);
|
2017-12-02 01:14:15 +01:00
|
|
|
} else {
|
2018-03-12 14:06:39 +01:00
|
|
|
if (element = this.tryResolveImport(queuedImport.referencedNameAlt, queuedExports)) {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(queuedImport.internalName, element);
|
2018-02-16 11:55:13 +01:00
|
|
|
queuedImports.splice(i, 1);
|
|
|
|
} else {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Module_0_has_no_exported_member_1,
|
|
|
|
queuedImport.declaration.range,
|
|
|
|
(<ImportStatement>queuedImport.declaration.parent).path.value,
|
|
|
|
queuedImport.declaration.externalName.text
|
|
|
|
);
|
2018-02-16 11:55:13 +01:00
|
|
|
++i;
|
|
|
|
}
|
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
|
2018-03-12 14:06:39 +01:00
|
|
|
for (let [exportName, queuedExport] of queuedExports) {
|
|
|
|
let currentExport: QueuedExport | null = queuedExport; // nullable below
|
|
|
|
let element: Element | null;
|
2017-12-02 01:14:15 +01:00
|
|
|
do {
|
|
|
|
if (currentExport.isReExport) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = this.fileLevelExports.get(currentExport.referencedName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
exportName,
|
|
|
|
element,
|
|
|
|
currentExport.member.externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2017-12-27 02:37:53 +01:00
|
|
|
currentExport = queuedExports.get(currentExport.referencedName);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!currentExport) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Module_0_has_no_exported_member_1,
|
|
|
|
queuedExport.member.externalName.range,
|
2018-03-12 14:06:39 +01:00
|
|
|
(<StringLiteralExpression>(<ExportStatement>queuedExport.member.parent).path).value,
|
2018-02-25 00:13:39 +01:00
|
|
|
queuedExport.member.externalName.text
|
|
|
|
);
|
|
|
|
}
|
2017-12-02 01:14:15 +01:00
|
|
|
} else {
|
2018-01-17 02:08:14 +01:00
|
|
|
if (
|
2018-02-25 00:13:39 +01:00
|
|
|
// normal export
|
2018-03-17 01:37:05 +01:00
|
|
|
(element = this.elementsLookup.get(currentExport.referencedName)) ||
|
2018-02-25 00:13:39 +01:00
|
|
|
// library re-export
|
2018-03-17 01:37:05 +01:00
|
|
|
(element = this.elementsLookup.get(currentExport.member.name.text))
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
exportName,
|
|
|
|
element,
|
|
|
|
currentExport.member.externalName
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Cannot_find_name_0,
|
|
|
|
queuedExport.member.range, queuedExport.member.name.text
|
|
|
|
);
|
|
|
|
}
|
2017-09-29 17:25:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-12-02 01:14:15 +01:00
|
|
|
} while (currentExport);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
2018-01-07 15:07:46 +01:00
|
|
|
|
|
|
|
// resolve base prototypes of derived classes
|
2018-04-11 23:35:19 +02:00
|
|
|
for (let i = 0, k = queuedExtends.length; i < k; ++i) {
|
|
|
|
let derivedPrototype = queuedExtends[i];
|
|
|
|
let derivedDeclaration = derivedPrototype.declaration;
|
2018-03-12 14:06:39 +01:00
|
|
|
let derivedType = assert(derivedDeclaration.extendsType);
|
2018-04-11 23:35:19 +02:00
|
|
|
let baseElement = this.resolveIdentifier(derivedType.name, null); // reports
|
|
|
|
if (!baseElement) continue;
|
|
|
|
if (baseElement.kind == ElementKind.CLASS_PROTOTYPE) {
|
|
|
|
let basePrototype = <ClassPrototype>baseElement;
|
|
|
|
derivedPrototype.basePrototype = basePrototype;
|
2018-04-05 02:23:03 +02:00
|
|
|
} else {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.A_class_may_only_extend_another_class,
|
|
|
|
derivedType.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
|
|
|
|
var globalAliases = options.globalAliases;
|
|
|
|
if (globalAliases) {
|
|
|
|
for (let [alias, name] of globalAliases) {
|
2018-05-21 23:14:47 +02:00
|
|
|
if (!name.length) continue; // explicitly disabled
|
2018-05-21 18:48:29 +02:00
|
|
|
let element = this.elementsLookup.get(name);
|
2018-03-26 16:54:25 +02:00
|
|
|
if (element) this.elementsLookup.set(alias, element);
|
2018-05-21 18:48:29 +02:00
|
|
|
else throw new Error("element not found: " + name);
|
2018-03-26 16:54:25 +02:00
|
|
|
}
|
|
|
|
}
|
2018-03-31 18:18:55 +02:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// register 'Array'
|
2018-03-31 18:18:55 +02:00
|
|
|
var arrayPrototype = this.elementsLookup.get("Array");
|
|
|
|
if (arrayPrototype) {
|
|
|
|
assert(arrayPrototype.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
this.arrayPrototype = <ClassPrototype>arrayPrototype;
|
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// register 'ArrayBufferView'
|
|
|
|
var arrayBufferViewPrototype = this.elementsLookup.get("ArrayBufferView");
|
|
|
|
if (arrayBufferViewPrototype) {
|
|
|
|
assert(arrayBufferViewPrototype.kind == ElementKind.INTERFACE_PROTOTYPE);
|
|
|
|
this.arrayBufferViewPrototype = <InterfacePrototype>arrayBufferViewPrototype;
|
|
|
|
}
|
|
|
|
|
|
|
|
// register 'String'
|
2018-03-31 18:18:55 +02:00
|
|
|
var stringPrototype = this.elementsLookup.get("String");
|
|
|
|
if (stringPrototype) {
|
|
|
|
assert(stringPrototype.kind == ElementKind.CLASS_PROTOTYPE);
|
|
|
|
let stringInstance = (<ClassPrototype>stringPrototype).resolve(null); // reports
|
|
|
|
if (stringInstance) {
|
|
|
|
if (this.typesLookup.has("string")) {
|
|
|
|
let declaration = (<ClassPrototype>stringPrototype).declaration;
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, declaration.programLevelInternalName
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
this.stringInstance = stringInstance;
|
|
|
|
this.typesLookup.set("string", stringInstance.type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Tries to resolve an import by traversing exports and queued exports. */
|
2018-02-25 00:13:39 +01:00
|
|
|
private tryResolveImport(
|
|
|
|
referencedName: string,
|
|
|
|
queuedExports: Map<string,QueuedExport>
|
|
|
|
): Element | null {
|
2017-12-28 04:09:40 +01:00
|
|
|
var element: Element | null;
|
2018-03-17 01:37:05 +01:00
|
|
|
var fileLevelExports = this.fileLevelExports;
|
2017-12-02 01:14:15 +01:00
|
|
|
do {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = fileLevelExports.get(referencedName)) return element;
|
2018-03-13 02:32:10 +01:00
|
|
|
let queuedExport = queuedExports.get(referencedName);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!queuedExport) return null;
|
2017-12-02 01:14:15 +01:00
|
|
|
if (queuedExport.isReExport) {
|
|
|
|
referencedName = queuedExport.referencedName;
|
|
|
|
continue;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
return this.elementsLookup.get(queuedExport.referencedName);
|
2017-12-02 01:14:15 +01:00
|
|
|
} while (true);
|
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
private filterDecorators(decorators: DecoratorNode[], acceptedFlags: DecoratorFlags): DecoratorFlags {
|
|
|
|
var presentFlags = DecoratorFlags.NONE;
|
|
|
|
for (let i = 0, k = decorators.length; i < k; ++i) {
|
|
|
|
let decorator = decorators[i];
|
2018-06-01 14:17:27 +02:00
|
|
|
let kind = decoratorNameToKind(decorator.name);
|
|
|
|
let flag = decoratorKindToFlag(kind);
|
|
|
|
if (flag) {
|
|
|
|
if (!(acceptedFlags & flag)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Decorator_0_is_not_valid_here,
|
|
|
|
decorator.range, decorator.name.range.toString()
|
|
|
|
);
|
|
|
|
} else if (presentFlags & flag) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_decorator,
|
|
|
|
decorator.range, decorator.name.range.toString()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
presentFlags |= flag;
|
2018-04-11 23:35:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return presentFlags;
|
|
|
|
}
|
|
|
|
|
2018-03-26 16:54:25 +02:00
|
|
|
/** Processes global options, if present. */
|
|
|
|
private checkGlobalOptions(
|
2018-02-25 00:13:39 +01:00
|
|
|
element: Element,
|
|
|
|
declaration: DeclarationStatement
|
|
|
|
): void {
|
2018-03-17 01:37:05 +01:00
|
|
|
var parentNode = declaration.parent;
|
2018-02-09 02:31:48 +01:00
|
|
|
if (
|
2018-04-11 23:35:19 +02:00
|
|
|
(element.hasDecorator(DecoratorFlags.GLOBAL)) ||
|
2018-04-27 00:08:41 +02:00
|
|
|
(declaration.range.source.is(CommonFlags.BUILTIN)) ||
|
2018-02-09 02:31:48 +01:00
|
|
|
(
|
|
|
|
declaration.range.source.isLibrary &&
|
2018-03-17 01:37:05 +01:00
|
|
|
element.is(CommonFlags.EXPORT) &&
|
2018-02-09 02:31:48 +01:00
|
|
|
(
|
2018-03-17 01:37:05 +01:00
|
|
|
assert(parentNode).kind == NodeKind.SOURCE ||
|
2018-02-09 02:31:48 +01:00
|
|
|
(
|
2018-03-17 01:37:05 +01:00
|
|
|
<Node>parentNode).kind == NodeKind.VARIABLE &&
|
|
|
|
assert((<Node>parentNode).parent).kind == NodeKind.SOURCE
|
2018-02-09 02:31:48 +01:00
|
|
|
)
|
|
|
|
)
|
|
|
|
) {
|
2018-04-27 00:08:41 +02:00
|
|
|
let globalName = declaration.programLevelInternalName;
|
|
|
|
if (this.elementsLookup.has(globalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, element.internalName
|
|
|
|
);
|
|
|
|
} else {
|
2018-04-27 00:08:41 +02:00
|
|
|
this.elementsLookup.set(globalName, element);
|
|
|
|
if (element.is(CommonFlags.BUILTIN)) element.internalName = globalName;
|
2018-01-17 02:08:14 +01:00
|
|
|
}
|
2018-01-12 15:36:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeClass(
|
|
|
|
declaration: ClassDeclaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
queuedExtends: ClassPrototype[],
|
|
|
|
queuedImplements: ClassPrototype[],
|
2018-02-25 00:13:39 +01:00
|
|
|
namespace: Element | null = null
|
|
|
|
): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-11-17 14:33:51 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
var decorators = declaration.decorators;
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
2018-02-25 00:13:39 +01:00
|
|
|
var prototype = new ClassPrototype(
|
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
2018-02-25 00:13:39 +01:00
|
|
|
internalName,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration,
|
|
|
|
decorators
|
|
|
|
? this.filterDecorators(decorators,
|
|
|
|
DecoratorFlags.GLOBAL |
|
|
|
|
DecoratorFlags.SEALED |
|
|
|
|
DecoratorFlags.UNMANAGED
|
|
|
|
)
|
|
|
|
: DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-04-15 00:34:19 +02:00
|
|
|
prototype.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, prototype);
|
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;
|
2018-04-11 23:35:19 +02:00
|
|
|
if (prototype.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
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
// remember classes that implement interfaces
|
2018-04-02 19:05:26 +02:00
|
|
|
} else if (numImplementsTypes) {
|
2018-04-11 23:35:19 +02:00
|
|
|
queuedImplements.push(prototype);
|
2018-03-17 01:37:05 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-16 20:08:33 +01:00
|
|
|
|
2018-01-07 15:07:46 +01:00
|
|
|
// remember classes that extend another one
|
2018-04-11 23:35:19 +02:00
|
|
|
if (declaration.extendsType) queuedExtends.push(prototype);
|
2018-01-07 15:07:46 +01:00
|
|
|
|
2017-12-27 02:37:53 +01:00
|
|
|
// add as namespace member if applicable
|
2017-12-15 02:50:55 +01:00
|
|
|
if (namespace) {
|
|
|
|
if (namespace.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (namespace.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
namespace.members.set(simpleName, prototype);
|
2018-04-15 00:34:19 +02:00
|
|
|
if (namespace.is(CommonFlags.MODULE_EXPORT) && prototype.is(CommonFlags.EXPORT)) {
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
2018-03-17 01:37:05 +01:00
|
|
|
}
|
2017-12-27 02:37:53 +01:00
|
|
|
|
|
|
|
// otherwise add to file-level exports if exported
|
2018-03-17 01:37:05 +01:00
|
|
|
} else if (prototype.is(CommonFlags.EXPORT)) {
|
|
|
|
if (this.fileLevelExports.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(internalName, prototype);
|
|
|
|
if (prototype.is(CommonFlags.EXPORT) && declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, prototype);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
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: {
|
2018-01-02 03:54:06 +01:00
|
|
|
this.initializeField(<FieldDeclaration>memberDeclaration, prototype);
|
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)) {
|
|
|
|
this.initializeAccessor(<MethodDeclaration>memberDeclaration, prototype);
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
|
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
|
|
|
}
|
|
|
|
default: {
|
2017-12-27 02:37:53 +01:00
|
|
|
throw new Error("class member expected");
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-27 05:35:14 +01:00
|
|
|
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(prototype, declaration);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeField(
|
|
|
|
declaration: FieldDeclaration,
|
|
|
|
classPrototype: ClassPrototype
|
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-04-24 23:11:11 +02:00
|
|
|
var decorators = declaration.decorators;
|
2017-12-15 02:50:55 +01:00
|
|
|
|
|
|
|
// static fields become global variables
|
2018-03-17 01:37:05 +01:00
|
|
|
if (declaration.is(CommonFlags.STATIC)) {
|
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (classPrototype.members) {
|
|
|
|
if (classPrototype.members.has(name)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-13 02:32:10 +01:00
|
|
|
let staticField = new Global(
|
2018-03-20 23:41:37 +01:00
|
|
|
this,
|
|
|
|
name,
|
|
|
|
internalName,
|
2018-04-03 23:56:48 +02:00
|
|
|
Type.void, // resolved later on
|
2018-04-24 23:11:11 +02:00
|
|
|
declaration,
|
|
|
|
decorators
|
|
|
|
? this.filterDecorators(decorators, DecoratorFlags.NONE)
|
|
|
|
: DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-04-15 00:34:19 +02:00
|
|
|
staticField.parent = classPrototype;
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.members.set(name, staticField);
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, staticField);
|
2018-04-15 00:34:19 +02:00
|
|
|
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
staticField.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
|
|
|
|
// instance fields are remembered until resolved
|
2017-10-19 18:55:27 +02:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (classPrototype.instanceMembers) {
|
|
|
|
if (classPrototype.instanceMembers.has(name)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.instanceMembers = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-13 02:32:10 +01:00
|
|
|
let instanceField = new FieldPrototype(
|
2018-02-25 00:13:39 +01:00
|
|
|
classPrototype,
|
2018-03-20 23:41:37 +01:00
|
|
|
name,
|
|
|
|
internalName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration
|
|
|
|
);
|
2018-04-24 23:11:11 +02:00
|
|
|
if (decorators) this.filterDecorators(decorators, DecoratorFlags.NONE);
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.instanceMembers.set(name, instanceField);
|
2018-04-15 00:34:19 +02:00
|
|
|
// TBD: no need to mark as MODULE_EXPORT
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeMethod(
|
|
|
|
declaration: MethodDeclaration,
|
|
|
|
classPrototype: ClassPrototype
|
|
|
|
): void {
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-01-28 06:18:27 +01:00
|
|
|
var prototype: FunctionPrototype | null = null;
|
2017-12-15 02:50:55 +01:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
var decorators = declaration.decorators;
|
|
|
|
var decoratorFlags = DecoratorFlags.NONE;
|
|
|
|
if (decorators) {
|
|
|
|
decoratorFlags = this.filterDecorators(decorators,
|
2018-06-04 18:23:09 +03:00
|
|
|
DecoratorFlags.OPERATOR_BINARY |
|
|
|
|
DecoratorFlags.OPERATOR_PREFIX |
|
|
|
|
DecoratorFlags.OPERATOR_POSTFIX |
|
2018-04-11 23:35:19 +02:00
|
|
|
DecoratorFlags.INLINE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-12-15 02:50:55 +01:00
|
|
|
// static methods become global functions
|
2018-03-17 01:37:05 +01:00
|
|
|
if (declaration.is(CommonFlags.STATIC)) {
|
2018-01-27 05:35:14 +01:00
|
|
|
assert(declaration.name.kind != NodeKind.CONSTRUCTOR);
|
2017-12-27 02:37:53 +01:00
|
|
|
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0, declaration.name.range,
|
|
|
|
internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
if (classPrototype.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (classPrototype.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
prototype = new FunctionPrototype(
|
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
|
|
|
internalName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
classPrototype,
|
|
|
|
decoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-03-17 01:37:05 +01:00
|
|
|
classPrototype.members.set(simpleName, prototype);
|
|
|
|
this.elementsLookup.set(internalName, prototype);
|
|
|
|
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
|
|
|
|
// instance methods are remembered until resolved
|
|
|
|
} else {
|
|
|
|
if (classPrototype.instanceMembers) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (classPrototype.instanceMembers.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
classPrototype.instanceMembers = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
prototype = new FunctionPrototype(
|
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
|
|
|
internalName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
classPrototype,
|
|
|
|
decoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-01-27 05:35:14 +01:00
|
|
|
// if (classPrototype.isUnmanaged && instancePrototype.isAbstract) {
|
|
|
|
// this.error( Unmanaged classes cannot declare abstract methods. );
|
2018-01-04 01:36:26 +01:00
|
|
|
// }
|
2018-01-27 05:35:14 +01:00
|
|
|
if (declaration.name.kind == NodeKind.CONSTRUCTOR) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (classPrototype.constructorPrototype) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Multiple_constructor_implementations_are_not_allowed,
|
|
|
|
declaration.name.range
|
|
|
|
);
|
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
prototype.set(CommonFlags.CONSTRUCTOR);
|
2018-01-28 06:18:27 +01:00
|
|
|
classPrototype.constructorPrototype = prototype;
|
2018-01-28 15:13:31 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
classPrototype.instanceMembers.set(simpleName, prototype);
|
|
|
|
}
|
|
|
|
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-01-06 10:20:38 +01:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
this.checkOperatorOverloads(declaration.decorators, prototype, classPrototype);
|
2018-01-28 06:18:27 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
private checkOperatorOverloads(
|
2018-03-12 14:06:39 +01:00
|
|
|
decorators: DecoratorNode[] | null,
|
2018-02-25 00:13:39 +01:00
|
|
|
prototype: FunctionPrototype,
|
|
|
|
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,
|
|
|
|
decorator.range, "1", numArgs.toString(0)
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-06 10:20:38 +01:00
|
|
|
}
|
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeAccessor(
|
|
|
|
declaration: MethodDeclaration,
|
2018-03-17 01:37:05 +01:00
|
|
|
classPrototype: ClassPrototype
|
2018-02-25 00:13:39 +01:00
|
|
|
): void {
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalPropertyName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
var propertyElement = this.elementsLookup.get(internalPropertyName);
|
|
|
|
var isGetter = declaration.is(CommonFlags.GET);
|
|
|
|
var isNew = false;
|
2017-12-27 02:37:53 +01:00
|
|
|
if (propertyElement) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (
|
|
|
|
propertyElement.kind != ElementKind.PROPERTY ||
|
|
|
|
(isGetter
|
|
|
|
? (<Property>propertyElement).getterPrototype
|
|
|
|
: (<Property>propertyElement).setterPrototype
|
2018-03-17 01:37:05 +01:00
|
|
|
) != null
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalPropertyName
|
|
|
|
);
|
2017-12-27 02:37:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
|
|
|
propertyElement = new Property(
|
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
|
|
|
internalPropertyName,
|
2018-02-25 00:13:39 +01:00
|
|
|
classPrototype
|
|
|
|
);
|
2018-03-17 01:37:05 +01:00
|
|
|
isNew = true;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-27 02:37:53 +01:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
var decorators = declaration.decorators;
|
|
|
|
var decoratorFlags = DecoratorFlags.NONE;
|
|
|
|
if (decorators) {
|
|
|
|
decoratorFlags = this.filterDecorators(decorators,
|
|
|
|
DecoratorFlags.INLINE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-03-17 01:37:05 +01:00
|
|
|
var baseName = (isGetter ? GETTER_PREFIX : SETTER_PREFIX) + simpleName;
|
2017-12-27 02:37:53 +01:00
|
|
|
|
|
|
|
// static accessors become global functions
|
2018-03-17 01:37:05 +01:00
|
|
|
if (declaration.is(CommonFlags.STATIC)) {
|
|
|
|
let staticName = classPrototype.internalName + STATIC_DELIMITER + baseName;
|
|
|
|
if (this.elementsLookup.has(staticName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, staticName
|
|
|
|
);
|
2017-12-27 02:37:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-13 02:32:10 +01:00
|
|
|
let staticPrototype = new FunctionPrototype(
|
2018-02-25 00:13:39 +01:00
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
baseName,
|
|
|
|
staticName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
null,
|
|
|
|
decoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
if (isGetter) {
|
2017-12-27 02:37:53 +01:00
|
|
|
(<Property>propertyElement).getterPrototype = staticPrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-27 02:37:53 +01:00
|
|
|
(<Property>propertyElement).setterPrototype = staticPrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
if (isNew) {
|
|
|
|
if (classPrototype.members) {
|
|
|
|
if (classPrototype.members.has(simpleName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, staticName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
classPrototype.members = new Map();
|
|
|
|
}
|
|
|
|
classPrototype.members.set(simpleName, propertyElement); // check above
|
|
|
|
} else {
|
|
|
|
assert(classPrototype.members && classPrototype.members.has(simpleName));
|
|
|
|
}
|
|
|
|
this.elementsLookup.set(internalPropertyName, propertyElement);
|
|
|
|
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
propertyElement.set(CommonFlags.MODULE_EXPORT);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-27 02:37:53 +01:00
|
|
|
|
|
|
|
// instance accessors are remembered until resolved
|
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
let instanceName = classPrototype.internalName + INSTANCE_DELIMITER + baseName;
|
2017-12-27 02:37:53 +01:00
|
|
|
if (classPrototype.instanceMembers) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (classPrototype.instanceMembers.has(baseName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalPropertyName
|
|
|
|
);
|
2017-12-27 02:37:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-27 02:37:53 +01:00
|
|
|
classPrototype.instanceMembers = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-13 02:32:10 +01:00
|
|
|
let instancePrototype = new FunctionPrototype(
|
2018-02-25 00:13:39 +01:00
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
baseName,
|
|
|
|
instanceName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
classPrototype,
|
|
|
|
decoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
if (isGetter) {
|
2017-12-27 02:37:53 +01:00
|
|
|
(<Property>propertyElement).getterPrototype = instancePrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-27 02:37:53 +01:00
|
|
|
(<Property>propertyElement).setterPrototype = instancePrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
classPrototype.instanceMembers.set(baseName, propertyElement);
|
|
|
|
this.elementsLookup.set(internalPropertyName, propertyElement);
|
|
|
|
if (classPrototype.is(CommonFlags.MODULE_EXPORT)) {
|
|
|
|
propertyElement.set(CommonFlags.MODULE_EXPORT);
|
2018-04-15 00:34:19 +02:00
|
|
|
instancePrototype.set(CommonFlags.MODULE_EXPORT);
|
2018-03-17 01:37:05 +01:00
|
|
|
}
|
2017-12-27 02:37:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeEnum(
|
|
|
|
declaration: EnumDeclaration,
|
|
|
|
namespace: Element | null = null
|
|
|
|
): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
|
|
|
var element = new Enum(this, simpleName, internalName, declaration);
|
2018-04-15 00:34:19 +02:00
|
|
|
element.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, element);
|
2017-12-13 23:24:13 +01:00
|
|
|
|
|
|
|
if (namespace) {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (namespace.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (namespace.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
namespace.members.set(simpleName, element);
|
2018-04-15 00:34:19 +02:00
|
|
|
if (namespace.is(CommonFlags.MODULE_EXPORT) && element.is(CommonFlags.EXPORT)) {
|
2018-03-17 01:37:05 +01:00
|
|
|
element.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
|
|
|
} else if (element.is(CommonFlags.EXPORT)) { // no namespace
|
|
|
|
if (this.fileLevelExports.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(internalName, element);
|
|
|
|
if (declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
element.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, element);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-13 23:24:13 +01:00
|
|
|
|
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
|
|
|
}
|
2018-03-21 16:29:08 +01:00
|
|
|
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(element, declaration);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeEnumValue(
|
|
|
|
declaration: EnumValueDeclaration,
|
|
|
|
enm: Enum
|
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2017-12-15 02:50:55 +01:00
|
|
|
if (enm.members) {
|
|
|
|
if (enm.members.has(name)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
enm.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-28 04:09:40 +01:00
|
|
|
var value = new EnumValue(enm, this, name, internalName, declaration);
|
2017-11-17 14:33:51 +01:00
|
|
|
enm.members.set(name, value);
|
2018-04-15 00:34:19 +02:00
|
|
|
if (enm.is(CommonFlags.MODULE_EXPORT)) {
|
2018-03-17 01:37:05 +01:00
|
|
|
value.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeExports(
|
|
|
|
statement: ExportStatement,
|
|
|
|
queuedExports: Map<string,QueuedExport>
|
|
|
|
): void {
|
2017-12-28 04:09:40 +01:00
|
|
|
var members = statement.members;
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0, k = members.length; i < k; ++i) {
|
2017-10-19 18:55:27 +02:00
|
|
|
this.initializeExport(members[i], statement.internalPath, queuedExports);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private setExportAndCheckLibrary(
|
|
|
|
name: string,
|
|
|
|
element: Element,
|
|
|
|
identifier: IdentifierExpression
|
|
|
|
): void {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(name, element);
|
2018-02-09 02:31:48 +01:00
|
|
|
if (identifier.range.source.isLibrary) { // add global alias
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(identifier.text)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
identifier.range, identifier.text
|
|
|
|
);
|
|
|
|
} else {
|
2018-02-14 19:21:31 +01:00
|
|
|
element.internalName = identifier.text;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(identifier.text, element);
|
2018-02-09 02:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeExport(
|
|
|
|
member: ExportMember,
|
|
|
|
internalPath: string | null,
|
|
|
|
queuedExports: Map<string,QueuedExport>
|
|
|
|
): void {
|
2018-02-14 19:21:31 +01:00
|
|
|
var externalName = member.range.source.internalPath + PATH_DELIMITER + member.externalName.text;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.fileLevelExports.has(externalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
member.externalName.range, externalName
|
|
|
|
);
|
2017-11-17 14:33:51 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-12-28 04:09:40 +01:00
|
|
|
var referencedName: string;
|
2018-02-09 02:31:48 +01:00
|
|
|
var referencedElement: Element | null;
|
2017-12-28 04:09:40 +01:00
|
|
|
var queuedExport: QueuedExport | null;
|
2017-12-02 01:14:15 +01:00
|
|
|
|
|
|
|
// export local element
|
2017-11-17 14:33:51 +01:00
|
|
|
if (internalPath == null) {
|
2018-02-14 19:21:31 +01:00
|
|
|
referencedName = member.range.source.internalPath + PATH_DELIMITER + member.name.text;
|
2017-12-02 01:14:15 +01:00
|
|
|
|
|
|
|
// resolve right away if the element exists
|
2018-03-17 01:37:05 +01:00
|
|
|
if (referencedElement = this.elementsLookup.get(referencedName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
externalName,
|
|
|
|
referencedElement,
|
|
|
|
member.externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise queue it
|
|
|
|
if (queuedExports.has(externalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
member.externalName.range, externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-12-28 04:09:40 +01:00
|
|
|
queuedExport = new QueuedExport();
|
2017-12-02 01:14:15 +01:00
|
|
|
queuedExport.isReExport = false;
|
|
|
|
queuedExport.referencedName = referencedName; // -> internal name
|
|
|
|
queuedExport.member = member;
|
|
|
|
queuedExports.set(externalName, queuedExport);
|
|
|
|
|
|
|
|
// export external element
|
2017-11-17 14:33:51 +01:00
|
|
|
} else {
|
2018-02-25 00:13:39 +01:00
|
|
|
referencedName = internalPath + PATH_DELIMITER + member.name.text;
|
2017-12-02 01:14:15 +01:00
|
|
|
|
|
|
|
// resolve right away if the export exists
|
2018-03-17 01:37:05 +01:00
|
|
|
referencedElement = this.elementsLookup.get(referencedName);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (referencedElement) {
|
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
externalName,
|
|
|
|
referencedElement,
|
|
|
|
member.externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// walk already known queued exports
|
2018-03-13 02:32:10 +01:00
|
|
|
let seen = new Set<QueuedExport>();
|
2017-12-27 02:37:53 +01:00
|
|
|
while (queuedExport = queuedExports.get(referencedName)) {
|
2017-12-02 01:14:15 +01:00
|
|
|
if (queuedExport.isReExport) {
|
2018-03-17 01:37:05 +01:00
|
|
|
referencedElement = this.fileLevelExports.get(queuedExport.referencedName);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (referencedElement) {
|
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
externalName,
|
|
|
|
referencedElement,
|
|
|
|
member.externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
referencedName = queuedExport.referencedName;
|
2018-02-25 00:13:39 +01:00
|
|
|
if (seen.has(queuedExport)) break;
|
2017-12-02 01:14:15 +01:00
|
|
|
seen.add(queuedExport);
|
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
referencedElement = this.elementsLookup.get(queuedExport.referencedName);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (referencedElement) {
|
|
|
|
this.setExportAndCheckLibrary(
|
|
|
|
externalName,
|
|
|
|
referencedElement,
|
|
|
|
member.externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise queue it
|
|
|
|
if (queuedExports.has(externalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
member.externalName.range, externalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-12-28 04:09:40 +01:00
|
|
|
queuedExport = new QueuedExport();
|
|
|
|
queuedExport.isReExport = true;
|
|
|
|
queuedExport.referencedName = referencedName; // -> export name
|
|
|
|
queuedExport.member = member;
|
|
|
|
queuedExports.set(externalName, queuedExport);
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeFunction(
|
|
|
|
declaration: FunctionDeclaration,
|
|
|
|
namespace: Element | null = null
|
|
|
|
): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-11-17 14:33:51 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
2018-04-11 23:35:19 +02:00
|
|
|
var decorators = declaration.decorators;
|
2018-02-25 00:13:39 +01:00
|
|
|
var prototype = new FunctionPrototype(
|
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
|
|
|
internalName,
|
2018-02-25 00:13:39 +01:00
|
|
|
declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
null,
|
|
|
|
decorators
|
|
|
|
? this.filterDecorators(decorators,
|
|
|
|
DecoratorFlags.GLOBAL |
|
|
|
|
DecoratorFlags.INLINE
|
|
|
|
)
|
|
|
|
: DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-04-15 00:34:19 +02:00
|
|
|
prototype.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, prototype);
|
2017-12-13 23:24:13 +01:00
|
|
|
|
|
|
|
if (namespace) {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (namespace.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (namespace.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
namespace.members.set(simpleName, prototype);
|
|
|
|
if (namespace.is(CommonFlags.MODULE_EXPORT) && prototype.is(CommonFlags.EXPORT)) {
|
2018-04-15 00:34:19 +02:00
|
|
|
prototype.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
|
|
|
} else if (prototype.is(CommonFlags.EXPORT)) { // no namespace
|
|
|
|
if (this.fileLevelExports.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(internalName, prototype);
|
|
|
|
if (declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, prototype);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-03-21 16:29:08 +01:00
|
|
|
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(prototype, declaration);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeImports(
|
|
|
|
statement: ImportStatement,
|
|
|
|
queuedExports: Map<string,QueuedExport>,
|
|
|
|
queuedImports: QueuedImport[]
|
|
|
|
): void {
|
2017-12-28 04:09:40 +01:00
|
|
|
var declarations = statement.declarations;
|
2017-12-23 00:48:54 +01:00
|
|
|
if (declarations) {
|
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],
|
|
|
|
statement.internalPath,
|
|
|
|
queuedExports, queuedImports
|
|
|
|
);
|
|
|
|
}
|
2018-04-27 00:08:41 +02:00
|
|
|
} else if (statement.namespaceName) { // import * as simpleName from "file"
|
|
|
|
let simpleName = statement.namespaceName.text;
|
2018-03-13 02:32:10 +01:00
|
|
|
let internalName = (
|
2018-02-25 00:13:39 +01:00
|
|
|
statement.range.source.internalPath +
|
|
|
|
PATH_DELIMITER +
|
2018-04-27 00:08:41 +02:00
|
|
|
simpleName
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
statement.namespaceName.range,
|
|
|
|
internalName
|
|
|
|
);
|
2017-12-23 00:48:54 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error( // TODO
|
|
|
|
DiagnosticCode.Operation_not_supported,
|
|
|
|
statement.range
|
|
|
|
);
|
2018-01-05 18:19:32 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeImport(
|
|
|
|
declaration: ImportDeclaration,
|
|
|
|
internalPath: string,
|
|
|
|
queuedExports: Map<string,QueuedExport>,
|
|
|
|
queuedImports: QueuedImport[]
|
|
|
|
): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-14 19:21:31 +01:00
|
|
|
var referencedName = internalPath + PATH_DELIMITER + declaration.externalName.text;
|
2017-12-02 01:14:15 +01:00
|
|
|
|
2018-02-16 11:55:13 +01:00
|
|
|
// resolve right away if the exact export exists
|
|
|
|
var element: Element | null;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = this.fileLevelExports.get(referencedName)) {
|
|
|
|
this.elementsLookup.set(internalName, element);
|
2017-12-02 01:14:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise queue it
|
2018-04-27 00:08:41 +02:00
|
|
|
const indexPart = PATH_DELIMITER + "index";
|
2017-12-28 04:09:40 +01:00
|
|
|
var queuedImport = new QueuedImport();
|
2017-12-02 01:14:15 +01:00
|
|
|
queuedImport.internalName = internalName;
|
2018-02-16 11:55:13 +01:00
|
|
|
if (internalPath.endsWith(indexPart)) {
|
|
|
|
queuedImport.referencedName = referencedName; // try exact first
|
2018-02-25 00:13:39 +01:00
|
|
|
queuedImport.referencedNameAlt = (
|
|
|
|
internalPath.substring(0, internalPath.length - indexPart.length + 1) +
|
|
|
|
declaration.externalName.text
|
|
|
|
);
|
2018-02-16 11:55:13 +01:00
|
|
|
} else {
|
|
|
|
queuedImport.referencedName = referencedName; // try exact first
|
2018-02-25 00:13:39 +01:00
|
|
|
queuedImport.referencedNameAlt = (
|
|
|
|
internalPath +
|
|
|
|
indexPart +
|
|
|
|
PATH_DELIMITER +
|
|
|
|
declaration.externalName.text
|
|
|
|
);
|
2018-02-16 11:55:13 +01:00
|
|
|
}
|
2017-12-02 01:14:15 +01:00
|
|
|
queuedImport.declaration = declaration;
|
|
|
|
queuedImports.push(queuedImport);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2017-12-15 02:50:55 +01:00
|
|
|
private initializeInterface(declaration: InterfaceDeclaration, namespace: Element | null = null): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
var decorators = declaration.decorators;
|
|
|
|
var prototype = new InterfacePrototype(
|
|
|
|
this,
|
|
|
|
declaration.name.text,
|
|
|
|
internalName,
|
|
|
|
declaration,
|
|
|
|
decorators
|
|
|
|
? this.filterDecorators(decorators, DecoratorFlags.GLOBAL)
|
|
|
|
: DecoratorFlags.NONE
|
|
|
|
);
|
2018-04-15 00:34:19 +02:00
|
|
|
prototype.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, prototype);
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
if (namespace) {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (namespace.members) {
|
|
|
|
if (namespace.members.has(prototype.internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members.set(prototype.internalName, prototype);
|
2018-03-17 01:37:05 +01:00
|
|
|
if (namespace.is(CommonFlags.MODULE_EXPORT) && prototype.is(CommonFlags.EXPORT)) {
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
|
|
|
} else if (prototype.is(CommonFlags.EXPORT)) { // no namespace
|
|
|
|
if (this.fileLevelExports.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(internalName, prototype);
|
|
|
|
if (declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prototype.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, prototype);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-13 23:24:13 +01:00
|
|
|
|
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) {
|
2017-09-28 13:08:25 +02:00
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.FIELDDECLARATION: {
|
2018-01-02 03:54:06 +01:00
|
|
|
this.initializeField(<FieldDeclaration>memberDeclaration, prototype);
|
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)) {
|
|
|
|
this.initializeAccessor(<MethodDeclaration>memberDeclaration, prototype);
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-01-02 03:54:06 +01:00
|
|
|
this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
|
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
|
|
|
}
|
|
|
|
default: {
|
2017-12-27 02:37:53 +01:00
|
|
|
throw new Error("interface member expected");
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
2018-03-21 16:29:08 +01:00
|
|
|
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(prototype, declaration);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
private initializeNamespace(
|
|
|
|
declaration: NamespaceDeclaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
queuedExtends: ClassPrototype[],
|
|
|
|
queuedImplements: ClassPrototype[],
|
2018-02-25 00:13:39 +01:00
|
|
|
parentNamespace: Element | null = null
|
|
|
|
): void {
|
2018-01-18 01:46:41 +01:00
|
|
|
var internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
var simpleName = declaration.name.text;
|
|
|
|
var namespace = this.elementsLookup.get(internalName);
|
2017-12-15 02:50:55 +01:00
|
|
|
if (!namespace) {
|
2018-03-17 01:37:05 +01:00
|
|
|
namespace = new Namespace(this, simpleName, internalName, declaration);
|
2018-04-15 00:34:19 +02:00
|
|
|
namespace.parent = parentNamespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, namespace);
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(namespace, declaration);
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
2017-12-13 23:24:13 +01:00
|
|
|
|
|
|
|
if (parentNamespace) {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (parentNamespace.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (parentNamespace.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
parentNamespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
parentNamespace.members.set(simpleName, namespace);
|
|
|
|
if (parentNamespace.is(CommonFlags.MODULE_EXPORT) && namespace.is(CommonFlags.EXPORT)) {
|
|
|
|
namespace.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
|
|
|
} else if (namespace.is(CommonFlags.EXPORT)) { // no parent namespace
|
2018-03-21 16:29:08 +01:00
|
|
|
let existingExport = this.fileLevelExports.get(internalName);
|
|
|
|
if (existingExport) {
|
|
|
|
if (!existingExport.is(CommonFlags.EXPORT)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local,
|
|
|
|
declaration.name.range, namespace.internalName
|
|
|
|
); // recoverable
|
|
|
|
}
|
|
|
|
namespace = existingExport; // join
|
|
|
|
} else {
|
|
|
|
this.fileLevelExports.set(internalName, namespace);
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
if (declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
namespace.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, namespace);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-12-13 23:24:13 +01:00
|
|
|
|
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: {
|
2018-04-11 23:35:19 +02:00
|
|
|
this.initializeClass(<ClassDeclaration>members[i], queuedExtends, queuedImplements, namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.ENUMDECLARATION: {
|
2017-12-15 02:50:55 +01:00
|
|
|
this.initializeEnum(<EnumDeclaration>members[i], namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.FUNCTIONDECLARATION: {
|
2017-12-15 02:50:55 +01:00
|
|
|
this.initializeFunction(<FunctionDeclaration>members[i], namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.INTERFACEDECLARATION: {
|
2017-12-15 02:50:55 +01:00
|
|
|
this.initializeInterface(<InterfaceDeclaration>members[i], namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.NAMESPACEDECLARATION: {
|
2018-04-11 23:35:19 +02:00
|
|
|
this.initializeNamespace(<NamespaceDeclaration>members[i], queuedExtends, queuedImplements, namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.TYPEDECLARATION: {
|
2018-01-02 03:54:06 +01:00
|
|
|
// this.initializeTypeAlias(<TypeDeclaration>members[i], namespace);
|
|
|
|
// TODO: what about namespaced types?
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Operation_not_supported,
|
|
|
|
members[i].range
|
|
|
|
);
|
2017-12-19 17:49:15 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.VARIABLE: {
|
2017-12-15 02:50:55 +01:00
|
|
|
this.initializeVariables(<VariableStatement>members[i], namespace);
|
2017-09-28 13:08:25 +02:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
default: {
|
2018-01-02 03:54:06 +01:00
|
|
|
throw new Error("namespace member expected");
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-01 20:27:21 +01:00
|
|
|
private initializeTypeAlias(declaration: TypeDeclaration, namespace: Element | null = null): void {
|
2017-12-19 17:49:15 +01:00
|
|
|
// type aliases are program globals
|
2017-12-27 19:17:29 +01:00
|
|
|
// TODO: what about namespaced types?
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = declaration.name.text;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.typesLookup.has(name) || this.typeAliases.has(name)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, name
|
|
|
|
);
|
2017-12-19 17:49:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-12 17:44:09 +01:00
|
|
|
var alias = new TypeAlias();
|
|
|
|
alias.typeParameters = declaration.typeParameters;
|
|
|
|
alias.type = declaration.type;
|
|
|
|
this.typeAliases.set(name, alias);
|
2017-12-19 17:49:15 +01:00
|
|
|
}
|
|
|
|
|
2017-12-15 02:50:55 +01:00
|
|
|
private initializeVariables(statement: VariableStatement, namespace: Element | null = null): 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];
|
2018-04-24 23:11:11 +02:00
|
|
|
let decorators = declaration.decorators;
|
2018-03-13 02:32:10 +01:00
|
|
|
let internalName = declaration.fileLevelInternalName;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.elementsLookup.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
let simpleName = declaration.name.text;
|
2018-03-13 02:32:10 +01:00
|
|
|
let global = new Global(
|
2018-02-25 00:13:39 +01:00
|
|
|
this,
|
2018-03-17 01:37:05 +01:00
|
|
|
simpleName,
|
2018-02-25 00:13:39 +01:00
|
|
|
internalName,
|
2018-04-03 23:56:48 +02:00
|
|
|
Type.void, // resolved later on
|
2018-04-24 23:11:11 +02:00
|
|
|
declaration,
|
|
|
|
decorators
|
|
|
|
? this.filterDecorators(decorators,
|
|
|
|
DecoratorFlags.GLOBAL
|
|
|
|
)
|
|
|
|
: DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-04-15 00:34:19 +02:00
|
|
|
global.parent = namespace;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.elementsLookup.set(internalName, global);
|
2017-12-13 23:24:13 +01:00
|
|
|
|
|
|
|
if (namespace) {
|
2017-12-15 02:50:55 +01:00
|
|
|
if (namespace.members) {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (namespace.members.has(simpleName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
2017-12-15 02:50:55 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2017-12-15 02:50:55 +01:00
|
|
|
namespace.members = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-17 01:37:05 +01:00
|
|
|
namespace.members.set(simpleName, global);
|
|
|
|
if (namespace.is(CommonFlags.MODULE_EXPORT) && global.is(CommonFlags.EXPORT)) {
|
|
|
|
global.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
}
|
|
|
|
} else if (global.is(CommonFlags.EXPORT)) { // no namespace
|
|
|
|
if (this.fileLevelExports.has(internalName)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
this.fileLevelExports.set(internalName, global);
|
|
|
|
}
|
|
|
|
if (declaration.range.source.isEntry) {
|
|
|
|
if (this.moduleLevelExports.has(internalName)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range, internalName
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
global.set(CommonFlags.MODULE_EXPORT);
|
|
|
|
this.moduleLevelExports.set(internalName, global);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-03-26 16:54:25 +02:00
|
|
|
this.checkGlobalOptions(global, declaration);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Resolves a {@link SignatureNode} to a concrete {@link Signature}. */
|
|
|
|
resolveSignature(
|
|
|
|
node: SignatureNode,
|
|
|
|
contextualTypeArguments: Map<string,Type> | null = null,
|
|
|
|
reportNotFound: bool = true
|
|
|
|
): Signature | null {
|
|
|
|
var explicitThisType = node.explicitThisType;
|
|
|
|
var thisType: Type | null = null;
|
|
|
|
if (explicitThisType) {
|
|
|
|
thisType = this.resolveType(
|
|
|
|
explicitThisType,
|
|
|
|
contextualTypeArguments,
|
|
|
|
reportNotFound
|
|
|
|
);
|
|
|
|
if (!thisType) return null;
|
|
|
|
}
|
2018-05-30 16:22:56 +02:00
|
|
|
var parameterTypeNodes = node.parameters;
|
2018-03-12 14:06:39 +01:00
|
|
|
var numParameters = parameterTypeNodes.length;
|
|
|
|
var parameterTypes = new Array<Type>(numParameters);
|
|
|
|
var parameterNames = new Array<string>(numParameters);
|
|
|
|
var requiredParameters = 0;
|
|
|
|
var hasRest = false;
|
|
|
|
for (let i = 0; i < numParameters; ++i) {
|
|
|
|
let parameterTypeNode = parameterTypeNodes[i];
|
|
|
|
switch (parameterTypeNode.parameterKind) {
|
|
|
|
case ParameterKind.DEFAULT: {
|
|
|
|
requiredParameters = i + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ParameterKind.REST: {
|
|
|
|
assert(i == numParameters);
|
|
|
|
hasRest = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let parameterType = this.resolveType(
|
|
|
|
assert(parameterTypeNode.type),
|
|
|
|
contextualTypeArguments,
|
|
|
|
reportNotFound
|
|
|
|
);
|
|
|
|
if (!parameterType) return null;
|
|
|
|
parameterTypes[i] = parameterType;
|
|
|
|
parameterNames[i] = parameterTypeNode.name.text;
|
|
|
|
}
|
|
|
|
var returnTypeNode = node.returnType;
|
|
|
|
var returnType: Type | null;
|
|
|
|
if (returnTypeNode) {
|
|
|
|
returnType = this.resolveType(
|
|
|
|
returnTypeNode,
|
|
|
|
contextualTypeArguments,
|
|
|
|
reportNotFound
|
|
|
|
);
|
|
|
|
if (!returnType) return null;
|
|
|
|
} else {
|
|
|
|
returnType = Type.void;
|
|
|
|
}
|
|
|
|
var signature = new Signature(parameterTypes, returnType, thisType);
|
|
|
|
signature.parameterNames = parameterNames;
|
|
|
|
signature.requiredParameters = requiredParameters;
|
|
|
|
signature.hasRest = hasRest;
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Resolves a {@link CommonTypeNode} to a concrete {@link Type}. */
|
2018-02-25 00:13:39 +01:00
|
|
|
resolveType(
|
2018-03-12 14:06:39 +01:00
|
|
|
node: CommonTypeNode,
|
2018-02-25 00:13:39 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null = null,
|
|
|
|
reportNotFound: bool = true
|
|
|
|
): Type | null {
|
2018-03-12 14:06:39 +01:00
|
|
|
if (node.kind == NodeKind.SIGNATURE) {
|
|
|
|
let signature = this.resolveSignature(<SignatureNode>node, contextualTypeArguments, reportNotFound);
|
|
|
|
if (!signature) return null;
|
|
|
|
return Type.u32.asFunction(signature);
|
|
|
|
}
|
|
|
|
var typeNode = <TypeNode>node;
|
2018-03-12 17:44:09 +01:00
|
|
|
var simpleName = typeNode.name.text;
|
|
|
|
var globalName = simpleName;
|
|
|
|
var localName = typeNode.range.source.internalPath + PATH_DELIMITER + simpleName;
|
2018-01-01 20:27:21 +01:00
|
|
|
|
|
|
|
var element: Element | null;
|
2018-05-22 12:06:03 +02:00
|
|
|
if (
|
|
|
|
(element = this.elementsLookup.get(localName)) || // file-global
|
|
|
|
(element = this.elementsLookup.get(globalName)) // program-global
|
|
|
|
) {
|
2018-01-01 20:27:21 +01:00
|
|
|
switch (element.kind) {
|
2018-05-22 12:06:03 +02:00
|
|
|
case ElementKind.ENUM: return Type.i32;
|
2018-03-12 14:06:39 +01:00
|
|
|
case ElementKind.CLASS_PROTOTYPE: {
|
2018-03-13 02:32:10 +01:00
|
|
|
let instance = (<ClassPrototype>element).resolveUsingTypeArguments(
|
2018-03-12 14:06:39 +01:00
|
|
|
typeNode.typeArguments,
|
2018-02-25 00:13:39 +01:00
|
|
|
contextualTypeArguments,
|
|
|
|
null
|
|
|
|
); // reports
|
2018-01-01 20:27:21 +01:00
|
|
|
return instance ? instance.type : null;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-03-12 17:44:09 +01:00
|
|
|
// check (global) type alias
|
|
|
|
var alias = this.typeAliases.get(simpleName);
|
|
|
|
if (alias) return this.resolveType(alias.type, contextualTypeArguments, reportNotFound);
|
|
|
|
|
2017-10-19 18:55:27 +02:00
|
|
|
// resolve parameters
|
2018-03-12 14:06:39 +01:00
|
|
|
if (typeNode.typeArguments) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let k = typeNode.typeArguments.length;
|
|
|
|
let paramTypes = new Array<Type>(k);
|
|
|
|
for (let i = 0; i < k; ++i) {
|
|
|
|
let paramType = this.resolveType( // reports
|
2018-03-12 14:06:39 +01:00
|
|
|
typeNode.typeArguments[i],
|
|
|
|
contextualTypeArguments,
|
|
|
|
reportNotFound
|
|
|
|
);
|
|
|
|
if (!paramType) return null;
|
|
|
|
paramTypes[i] = paramType;
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
if (k) { // can't be a placeholder if it has parameters
|
2018-03-13 02:32:10 +01:00
|
|
|
let instanceKey = typesToString(paramTypes);
|
2018-03-12 14:06:39 +01:00
|
|
|
if (instanceKey.length) {
|
|
|
|
localName += "<" + instanceKey + ">";
|
|
|
|
globalName += "<" + instanceKey + ">";
|
|
|
|
}
|
|
|
|
} else if (contextualTypeArguments) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let placeholderType = contextualTypeArguments.get(globalName);
|
2018-03-12 14:06:39 +01:00
|
|
|
if (placeholderType) return placeholderType;
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-12-28 04:09:40 +01:00
|
|
|
var type: Type | null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-01-01 20:27:21 +01:00
|
|
|
// check file-global / program-global type
|
2018-03-17 01:37:05 +01:00
|
|
|
if ((type = this.typesLookup.get(localName)) || (type = this.typesLookup.get(globalName))) {
|
2017-10-19 18:55:27 +02:00
|
|
|
return type;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
if (reportNotFound) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Cannot_find_name_0,
|
2018-03-12 14:06:39 +01:00
|
|
|
typeNode.name.range, globalName
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
return null;
|
2017-09-29 17:25:02 +02:00
|
|
|
}
|
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves an array of type arguments to concrete types. */
|
2018-02-25 00:13:39 +01:00
|
|
|
resolveTypeArguments(
|
2018-03-12 14:06:39 +01:00
|
|
|
typeParameters: TypeParameterNode[],
|
|
|
|
typeArgumentNodes: CommonTypeNode[] | null,
|
2018-02-25 00:13:39 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null = null,
|
|
|
|
alternativeReportNode: Node | null = null
|
|
|
|
): Type[] | null {
|
2017-12-28 04:09:40 +01:00
|
|
|
var parameterCount = typeParameters.length;
|
|
|
|
var argumentCount = typeArgumentNodes ? typeArgumentNodes.length : 0;
|
2017-10-19 18:55:27 +02:00
|
|
|
if (parameterCount != argumentCount) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (argumentCount) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
|
|
|
Range.join(
|
|
|
|
(<TypeNode[]>typeArgumentNodes)[0].range,
|
|
|
|
(<TypeNode[]>typeArgumentNodes)[argumentCount - 1].range
|
|
|
|
),
|
|
|
|
parameterCount.toString(10), argumentCount.toString(10)
|
|
|
|
);
|
|
|
|
} else if (alternativeReportNode) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
|
|
|
alternativeReportNode.range.atEnd, parameterCount.toString(10), "0"
|
|
|
|
);
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
return null;
|
|
|
|
}
|
2017-12-28 04:09:40 +01:00
|
|
|
var typeArguments = new Array<Type>(parameterCount);
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0; i < parameterCount; ++i) {
|
|
|
|
let type = this.resolveType( // reports
|
2018-02-25 00:13:39 +01:00
|
|
|
(<TypeNode[]>typeArgumentNodes)[i],
|
|
|
|
contextualTypeArguments,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
if (!type) return null;
|
2017-10-19 18:55:27 +02:00
|
|
|
// TODO: check extendsType
|
|
|
|
typeArguments[i] = type;
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
return typeArguments;
|
|
|
|
}
|
|
|
|
|
2017-12-27 19:17:29 +01:00
|
|
|
/** Resolves an identifier to the element it refers to. */
|
2018-02-25 00:13:39 +01:00
|
|
|
resolveIdentifier(
|
|
|
|
identifier: IdentifierExpression,
|
|
|
|
contextualFunction: Function | null,
|
|
|
|
contextualEnum: Enum | null = null
|
2018-04-05 02:23:03 +02:00
|
|
|
): Element | null {
|
2018-02-14 19:21:31 +01:00
|
|
|
var name = identifier.text;
|
2017-12-27 19:17:29 +01:00
|
|
|
|
2017-12-28 04:09:40 +01:00
|
|
|
var element: Element | null;
|
|
|
|
var namespace: Element | null;
|
2017-12-27 19:17:29 +01:00
|
|
|
|
2018-01-24 03:08:09 +01:00
|
|
|
// check siblings
|
|
|
|
if (contextualEnum) {
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
if (
|
|
|
|
contextualEnum.members &&
|
|
|
|
(element = contextualEnum.members.get(name)) &&
|
|
|
|
element.kind == ElementKind.ENUMVALUE
|
|
|
|
) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return element; // ENUMVALUE
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-24 03:08:09 +01:00
|
|
|
|
|
|
|
} else if (contextualFunction) {
|
2018-01-18 23:34:12 +01:00
|
|
|
|
2018-01-07 15:07:46 +01:00
|
|
|
// check locals
|
2018-02-25 00:13:39 +01:00
|
|
|
if (element = contextualFunction.flow.getScopedLocal(name)) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return element; // LOCAL
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-07 15:07:46 +01:00
|
|
|
|
2018-04-03 23:56:48 +02:00
|
|
|
// check outer scope locals
|
|
|
|
// let outerScope = contextualFunction.outerScope;
|
|
|
|
// while (outerScope) {
|
|
|
|
// if (element = outerScope.getScopedLocal(name)) {
|
|
|
|
// let scopedLocal = <Local>element;
|
|
|
|
// let scopedGlobal = scopedLocal.scopedGlobal;
|
|
|
|
// if (!scopedGlobal) scopedGlobal = outerScope.addScopedGlobal(scopedLocal);
|
|
|
|
// if (!resolvedElement) resolvedElement = new ResolvedElement();
|
|
|
|
// return resolvedElement.set(scopedGlobal);
|
|
|
|
// }
|
|
|
|
// outerScope = outerScope.currentFunction.outerScope;
|
|
|
|
// }
|
|
|
|
|
2018-01-07 15:07:46 +01:00
|
|
|
// search contextual parent namespaces if applicable
|
2018-04-15 00:34:19 +02:00
|
|
|
if (namespace = contextualFunction.prototype.parent) {
|
2018-01-07 15:07:46 +01:00
|
|
|
do {
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = this.elementsLookup.get(namespace.internalName + STATIC_DELIMITER + name)) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return element; // LOCAL
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-04-15 00:34:19 +02:00
|
|
|
} while (namespace = namespace.parent);
|
2018-01-07 15:07:46 +01:00
|
|
|
}
|
2017-12-27 19:17:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// search current file
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = this.elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-27 19:17:29 +01:00
|
|
|
|
|
|
|
// search global scope
|
2018-03-17 01:37:05 +01:00
|
|
|
if (element = this.elementsLookup.get(name)) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-12-27 19:17:29 +01:00
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Cannot_find_name_0,
|
|
|
|
identifier.range, name
|
|
|
|
);
|
2017-12-13 23:24:13 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-01-01 20:27:21 +01:00
|
|
|
/** Resolves a property access to the element it refers to. */
|
2018-02-25 00:13:39 +01:00
|
|
|
resolvePropertyAccess(
|
|
|
|
propertyAccess: PropertyAccessExpression,
|
|
|
|
contextualFunction: Function
|
2018-04-05 02:23:03 +02:00
|
|
|
): Element | null {
|
2018-01-03 18:33:27 +01:00
|
|
|
// start by resolving the lhs target (expression before the last dot)
|
|
|
|
var targetExpression = propertyAccess.expression;
|
2018-04-05 02:23:03 +02:00
|
|
|
var target = this.resolveExpression(targetExpression, contextualFunction); // reports
|
|
|
|
if (!target) return null;
|
2018-01-02 03:54:06 +01:00
|
|
|
|
2018-01-03 18:33:27 +01:00
|
|
|
// at this point we know exactly what the target is, so look up the element within
|
2018-02-14 19:21:31 +01:00
|
|
|
var propertyName = propertyAccess.property.text;
|
2018-01-17 02:08:14 +01:00
|
|
|
|
2018-04-05 02:23:03 +02:00
|
|
|
// Resolve variable-likes to the class type they reference first
|
2018-01-01 20:27:21 +01:00
|
|
|
switch (target.kind) {
|
|
|
|
case ElementKind.GLOBAL:
|
|
|
|
case ElementKind.LOCAL:
|
2018-03-12 14:06:39 +01:00
|
|
|
case ElementKind.FIELD: {
|
2018-04-05 02:23:03 +02:00
|
|
|
let classReference = (<VariableLikeElement>target).type.classReference;
|
|
|
|
if (!classReference) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
2018-04-05 02:23:03 +02:00
|
|
|
propertyAccess.property.range, propertyName, (<VariableLikeElement>target).type.toString()
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-01-17 02:08:14 +01:00
|
|
|
return null;
|
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
target = classReference;
|
2018-01-17 02:08:14 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case ElementKind.PROPERTY: {
|
2018-03-13 02:32:10 +01:00
|
|
|
let getter = assert((<Property>target).getterPrototype).resolve(); // reports
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!getter) return null;
|
2018-04-05 02:23:03 +02:00
|
|
|
let classReference = getter.signature.returnType.classReference;
|
|
|
|
if (!classReference) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
2018-04-05 02:23:03 +02:00
|
|
|
propertyAccess.property.range, propertyName, getter.signature.returnType.toString()
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-01-17 02:08:14 +01:00
|
|
|
return null;
|
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
target = classReference;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.CLASS: {
|
|
|
|
let elementExpression = this.resolvedElementExpression;
|
|
|
|
if (elementExpression) {
|
2018-04-11 23:35:19 +02:00
|
|
|
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
|
|
|
if (!indexedGet) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
|
|
|
elementExpression.range, (<Class>target).internalName
|
|
|
|
);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
let returnType = indexedGet.signature.returnType;
|
|
|
|
if (!(target = returnType.classReference)) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
|
|
|
propertyAccess.property.range, propertyName, returnType.toString()
|
|
|
|
);
|
|
|
|
return null;
|
2018-04-05 02:23:03 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-17 02:08:14 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-01-17 02:08:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Look up the member within
|
|
|
|
switch (target.kind) {
|
2018-01-07 15:07:46 +01:00
|
|
|
case ElementKind.CLASS_PROTOTYPE:
|
2018-03-12 14:06:39 +01:00
|
|
|
case ElementKind.CLASS: {
|
2018-01-07 15:07:46 +01:00
|
|
|
do {
|
2018-04-05 02:23:03 +02:00
|
|
|
let members = target.members;
|
|
|
|
let member: Element | null;
|
|
|
|
if (members && (member = members.get(propertyName))) {
|
|
|
|
this.resolvedThisExpression = targetExpression;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return member; // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE...
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
// traverse inherited static members on the base prototype if target is a class prototype
|
2018-01-07 15:07:46 +01:00
|
|
|
if (target.kind == ElementKind.CLASS_PROTOTYPE) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if ((<ClassPrototype>target).basePrototype) {
|
2018-01-07 15:07:46 +01:00
|
|
|
target = <ClassPrototype>(<ClassPrototype>target).basePrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-01-07 15:07:46 +01:00
|
|
|
break;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
// traverse inherited instance members on the base class if target is a class instance
|
2018-01-07 15:07:46 +01:00
|
|
|
} else if (target.kind == ElementKind.CLASS) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if ((<Class>target).base) {
|
2018-01-07 15:07:46 +01:00
|
|
|
target = <Class>(<Class>target).base;
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-01-07 15:07:46 +01:00
|
|
|
break;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
} else {
|
2018-01-07 15:07:46 +01:00
|
|
|
break;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-07 15:07:46 +01:00
|
|
|
} while (true);
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
default: { // enums or other namespace-like elements
|
2018-04-05 02:23:03 +02:00
|
|
|
let members = target.members;
|
|
|
|
let member: Element | null;
|
|
|
|
if (members && (member = members.get(propertyName))) {
|
|
|
|
this.resolvedThisExpression = targetExpression;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE...
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-12-15 02:50:55 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
|
|
|
propertyAccess.property.range, propertyName, target.internalName
|
|
|
|
);
|
2017-12-13 23:24:13 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
resolveElementAccess(
|
|
|
|
elementAccess: ElementAccessExpression,
|
|
|
|
contextualFunction: Function
|
2018-04-05 02:23:03 +02:00
|
|
|
): Element | null {
|
2018-01-03 18:33:27 +01:00
|
|
|
var targetExpression = elementAccess.expression;
|
2018-04-05 02:23:03 +02:00
|
|
|
var target = this.resolveExpression(targetExpression, contextualFunction);
|
|
|
|
if (!target) return null;
|
2018-01-05 18:19:32 +01:00
|
|
|
switch (target.kind) {
|
2018-01-13 23:38:07 +01:00
|
|
|
case ElementKind.GLOBAL:
|
|
|
|
case ElementKind.LOCAL:
|
2018-03-12 14:06:39 +01:00
|
|
|
case ElementKind.FIELD: {
|
2018-03-13 02:32:10 +01:00
|
|
|
let type = (<VariableLikeElement>target).type;
|
2018-04-05 02:23:03 +02:00
|
|
|
if (target = type.classReference) {
|
|
|
|
this.resolvedThisExpression = targetExpression;
|
|
|
|
this.resolvedElementExpression = elementAccess.elementExpression;
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementKind.CLASS: { // element access on element access
|
2018-04-11 23:35:19 +02:00
|
|
|
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
|
|
|
if (!indexedGet) {
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
|
|
|
elementAccess.range, (<Class>target).internalName
|
|
|
|
);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
let returnType = indexedGet.signature.returnType;
|
|
|
|
if (target = returnType.classReference) {
|
|
|
|
this.resolvedThisExpression = targetExpression;
|
|
|
|
this.resolvedElementExpression = elementAccess.elementExpression;
|
|
|
|
return target;
|
2018-01-05 18:19:32 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-01-05 18:19:32 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
2018-04-05 02:23:03 +02:00
|
|
|
DiagnosticCode.Operation_not_supported,
|
|
|
|
targetExpression.range
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-01-05 18:19:32 +01:00
|
|
|
return null;
|
2018-01-03 18:33:27 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
resolveExpression(
|
|
|
|
expression: Expression,
|
|
|
|
contextualFunction: Function
|
2018-04-05 02:23:03 +02:00
|
|
|
): Element | null {
|
2018-02-25 00:13:39 +01:00
|
|
|
while (expression.kind == NodeKind.PARENTHESIZED) {
|
2018-01-29 22:36:07 +01:00
|
|
|
expression = (<ParenthesizedExpression>expression).expression;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-03 18:33:27 +01:00
|
|
|
switch (expression.kind) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.ASSERTION: {
|
2018-03-12 19:39:05 +01:00
|
|
|
let type = this.resolveType((<AssertionExpression>expression).toType); // reports
|
|
|
|
if (type) {
|
2018-03-21 23:27:53 +01:00
|
|
|
let classType = type.classReference;
|
2018-03-12 19:39:05 +01:00
|
|
|
if (classType) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return classType;
|
2018-03-12 19:39:05 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-29 22:36:07 +01:00
|
|
|
return null;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.BINARY: { // TODO: string concatenation, mostly
|
2018-01-29 22:36:07 +01:00
|
|
|
throw new Error("not implemented");
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-03-20 12:02:05 +01:00
|
|
|
case NodeKind.THIS: { // -> Class / ClassPrototype
|
2018-04-11 23:35:19 +02:00
|
|
|
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
|
|
|
|
let explicitLocal = contextualFunction.flow.getScopedLocal("this");
|
|
|
|
if (explicitLocal) {
|
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return explicitLocal;
|
|
|
|
}
|
|
|
|
}
|
2018-04-15 00:34:19 +02:00
|
|
|
let parent = contextualFunction.parent;
|
2018-03-20 12:02:05 +01:00
|
|
|
if (parent) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return parent;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode._this_cannot_be_referenced_in_current_location,
|
|
|
|
expression.range
|
|
|
|
);
|
2018-01-03 18:33:27 +01:00
|
|
|
return null;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.SUPER: { // -> Class
|
2018-04-11 23:35:19 +02:00
|
|
|
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
|
|
|
|
let explicitLocal = contextualFunction.flow.getScopedLocal("super");
|
|
|
|
if (explicitLocal) {
|
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return explicitLocal;
|
|
|
|
}
|
|
|
|
}
|
2018-04-15 00:34:19 +02:00
|
|
|
let parent = contextualFunction.parent;
|
2018-03-20 12:02:05 +01:00
|
|
|
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
|
2018-04-05 02:23:03 +02:00
|
|
|
this.resolvedThisExpression = null;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return parent;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
this.error(
|
|
|
|
DiagnosticCode._super_can_only_be_referenced_in_a_derived_class,
|
|
|
|
expression.range
|
|
|
|
);
|
2018-01-03 18:33:27 +01:00
|
|
|
return null;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.IDENTIFIER: {
|
2018-01-03 18:33:27 +01:00
|
|
|
return this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-04-06 00:19:45 +02:00
|
|
|
case NodeKind.LITERAL: {
|
|
|
|
switch ((<LiteralExpression>expression).literalKind) {
|
|
|
|
case LiteralKind.STRING: {
|
|
|
|
this.resolvedThisExpression = expression;
|
|
|
|
this.resolvedElementExpression = null;
|
|
|
|
return this.stringInstance;
|
|
|
|
}
|
|
|
|
// case LiteralKind.ARRAY: // TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-03-12 14:06:39 +01:00
|
|
|
case NodeKind.PROPERTYACCESS: {
|
2018-03-03 18:38:38 +01:00
|
|
|
return this.resolvePropertyAccess(
|
|
|
|
<PropertyAccessExpression>expression,
|
|
|
|
contextualFunction
|
|
|
|
);
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.ELEMENTACCESS: {
|
2018-03-03 18:38:38 +01:00
|
|
|
return this.resolveElementAccess(
|
|
|
|
<ElementAccessExpression>expression,
|
|
|
|
contextualFunction
|
|
|
|
);
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
case NodeKind.CALL: {
|
2018-04-05 02:23:03 +02:00
|
|
|
let targetExpression = (<CallExpression>expression).expression;
|
|
|
|
let target = this.resolveExpression(targetExpression, contextualFunction); // reports
|
|
|
|
if (!target) return null;
|
|
|
|
if (target.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
|
|
|
let instance = (<FunctionPrototype>target).resolveUsingTypeArguments( // reports
|
|
|
|
(<CallExpression>expression).typeArguments,
|
2018-04-11 23:35:19 +02:00
|
|
|
contextualFunction.flow.contextualTypeArguments,
|
2018-04-05 02:23:03 +02:00
|
|
|
expression
|
|
|
|
);
|
|
|
|
if (!instance) return null;
|
|
|
|
let returnType = instance.signature.returnType;
|
|
|
|
let classType = returnType.classReference;
|
|
|
|
if (classType) {
|
|
|
|
// reuse resolvedThisExpression (might be property access)
|
|
|
|
// reuse resolvedElementExpression (might be element access)
|
|
|
|
return classType;
|
|
|
|
} else {
|
|
|
|
let signature = returnType.signatureReference;
|
|
|
|
if (signature) {
|
|
|
|
let functionTarget = signature.cachedFunctionTarget;
|
|
|
|
if (!functionTarget) {
|
|
|
|
functionTarget = new FunctionTarget(this, signature);
|
|
|
|
signature.cachedFunctionTarget = functionTarget;
|
2018-03-12 19:39:05 +01:00
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
// reuse resolvedThisExpression (might be property access)
|
|
|
|
// reuse resolvedElementExpression (might be element access)
|
|
|
|
return functionTarget;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-02-09 02:31:48 +01:00
|
|
|
}
|
2018-04-05 02:23:03 +02:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
|
|
|
|
targetExpression.range, target.internalName
|
|
|
|
);
|
|
|
|
return null;
|
2018-02-09 02:31:48 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
this.error(
|
|
|
|
DiagnosticCode.Operation_not_supported,
|
|
|
|
expression.range
|
|
|
|
);
|
2018-01-05 18:19:32 +01:00
|
|
|
return null;
|
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,
|
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}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
NAMESPACE
|
|
|
|
}
|
|
|
|
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Indicates traits of a {@link Node} or {@link Element}. */
|
|
|
|
export enum CommonFlags {
|
2017-12-13 04:46:05 +01:00
|
|
|
/** No flags set. */
|
|
|
|
NONE = 0,
|
2018-03-17 01:37:05 +01:00
|
|
|
|
|
|
|
// Basic modifiers
|
|
|
|
|
|
|
|
/** Has an `import` modifier. */
|
|
|
|
IMPORT = 1 << 0,
|
|
|
|
/** Has an `export` modifier. */
|
|
|
|
EXPORT = 1 << 1,
|
|
|
|
/** Has a `declare` modifier. */
|
|
|
|
DECLARE = 1 << 2,
|
|
|
|
/** Has a `const` modifier. */
|
|
|
|
CONST = 1 << 3,
|
|
|
|
/** Has a `let` modifier. */
|
|
|
|
LET = 1 << 4,
|
|
|
|
/** Has a `static` modifier. */
|
|
|
|
STATIC = 1 << 5,
|
|
|
|
/** Has a `readonly` modifier. */
|
|
|
|
READONLY = 1 << 6,
|
|
|
|
/** Has an `abstract` modifier. */
|
|
|
|
ABSTRACT = 1 << 7,
|
|
|
|
/** Has a `public` modifier. */
|
|
|
|
PUBLIC = 1 << 8,
|
|
|
|
/** Has a `private` modifier. */
|
|
|
|
PRIVATE = 1 << 9,
|
|
|
|
/** Has a `protected` modifier. */
|
|
|
|
PROTECTED = 1 << 10,
|
|
|
|
/** Has a `get` modifier. */
|
|
|
|
GET = 1 << 11,
|
|
|
|
/** Has a `set` modifier. */
|
|
|
|
SET = 1 << 12,
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// Extended modifiers usually derived from basic modifiers
|
2018-03-17 01:37:05 +01:00
|
|
|
|
|
|
|
/** Is ambient, that is either declared or nested in a declared element. */
|
2018-04-11 23:35:19 +02:00
|
|
|
AMBIENT = 1 << 13,
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Is generic. */
|
2018-04-11 23:35:19 +02:00
|
|
|
GENERIC = 1 << 14,
|
2018-03-17 14:40:58 +01:00
|
|
|
/** Is part of a generic context. */
|
2018-04-11 23:35:19 +02:00
|
|
|
GENERIC_CONTEXT = 1 << 15,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Is an instance member. */
|
2018-04-11 23:35:19 +02:00
|
|
|
INSTANCE = 1 << 16,
|
2018-01-28 15:13:31 +01:00
|
|
|
/** Is a constructor. */
|
2018-04-11 23:35:19 +02:00
|
|
|
CONSTRUCTOR = 1 << 17,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Is an arrow function. */
|
2018-04-11 23:35:19 +02:00
|
|
|
ARROW = 1 << 18,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Is a module export. */
|
2018-04-11 23:35:19 +02:00
|
|
|
MODULE_EXPORT = 1 << 19,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Is a module import. */
|
2018-04-11 23:35:19 +02:00
|
|
|
MODULE_IMPORT = 1 << 20,
|
2018-03-17 01:37:05 +01:00
|
|
|
|
|
|
|
// Compilation states
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Is a builtin. */
|
|
|
|
BUILTIN = 1 << 21,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Is compiled. */
|
2018-04-11 23:35:19 +02:00
|
|
|
COMPILED = 1 << 22,
|
2018-03-17 01:37:05 +01:00
|
|
|
/** Has a constant value and is therefore inlined. */
|
2018-04-11 23:35:19 +02:00
|
|
|
INLINED = 1 << 23,
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Is scoped. */
|
2018-04-11 23:35:19 +02:00
|
|
|
SCOPED = 1 << 24,
|
2018-03-24 09:46:22 +01:00
|
|
|
/** Is a trampoline. */
|
2018-05-25 15:59:17 +02:00
|
|
|
TRAMPOLINE = 1 << 25,
|
|
|
|
/** Is a virtual method. */
|
|
|
|
VIRTUAL = 1 << 26
|
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-01 14:17:27 +02:00
|
|
|
INLINE = 1 << 6
|
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;
|
|
|
|
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 {
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Specific element kind. */
|
2017-10-19 18:55:27 +02:00
|
|
|
kind: ElementKind;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Containing {@link Program}. */
|
2017-10-19 18:55:27 +02:00
|
|
|
program: Program;
|
2017-12-27 22:38:32 +01:00
|
|
|
/** Simple name. */
|
|
|
|
simpleName: string;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Internal name referring to this element. */
|
2017-10-19 18:55:27 +02:00
|
|
|
internalName: string;
|
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;
|
2017-12-15 02:50:55 +01:00
|
|
|
/** Namespaced member elements. */
|
|
|
|
members: Map<string,Element> | null = null;
|
2018-04-15 00:34:19 +02:00
|
|
|
/** Parent element, if applicable. */
|
|
|
|
parent: Element | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new element, linking it to its containing {@link Program}. */
|
2017-12-27 22:38:32 +01:00
|
|
|
protected constructor(program: Program, simpleName: string, internalName: string) {
|
2017-10-19 18:55:27 +02:00
|
|
|
this.program = program;
|
2017-12-27 22:38:32 +01:00
|
|
|
this.simpleName = simpleName;
|
2017-10-19 18:55:27 +02:00
|
|
|
this.internalName = internalName;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2017-12-16 02:27:39 +01:00
|
|
|
/** A namespace. */
|
2017-11-17 14:33:51 +01:00
|
|
|
export class Namespace extends Element {
|
|
|
|
|
2017-12-27 19:17:29 +01:00
|
|
|
// All elements have namespace semantics. This is an explicitly declared one.
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.NAMESPACE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: NamespaceDeclaration; // more specific
|
2017-11-17 14:33:51 +01:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new namespace. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
declaration: NamespaceDeclaration
|
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2018-02-09 02:31:48 +01:00
|
|
|
this.declaration = declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.flags = declaration.flags;
|
2017-11-17 14:33:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** An enum. */
|
2017-12-15 02:50:55 +01:00
|
|
|
export class Enum extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
|
|
|
kind = ElementKind.ENUM;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: EnumDeclaration;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new enum. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
declaration: EnumDeclaration
|
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2018-02-09 02:31:48 +01:00
|
|
|
this.declaration = declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.flags = declaration.flags;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** An enum value. */
|
2017-10-19 18:55:27 +02:00
|
|
|
export class EnumValue extends Element {
|
|
|
|
|
|
|
|
kind = ElementKind.ENUMVALUE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: EnumValueDeclaration;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constant value, if applicable. */
|
2017-10-19 18:55:27 +02:00
|
|
|
constantValue: i32 = 0;
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
enm: Enum,
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
declaration: EnumValueDeclaration
|
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2018-04-15 00:34:19 +02:00
|
|
|
this.parent = enm;
|
2017-12-16 02:27:39 +01:00
|
|
|
this.declaration = declaration;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 09:18:43 +01:00
|
|
|
export const enum ConstantValueKind {
|
|
|
|
NONE,
|
|
|
|
INTEGER,
|
|
|
|
FLOAT
|
|
|
|
}
|
|
|
|
|
2017-12-28 15:17:35 +01:00
|
|
|
export class VariableLikeElement extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-28 15:17:35 +01:00
|
|
|
// kind varies
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-04-03 23:56:48 +02:00
|
|
|
declaration: VariableLikeDeclarationStatement | null;
|
2018-01-04 06:00:42 +01:00
|
|
|
/** Variable type. Is {@link Type.void} for type-inferred {@link Global}s before compilation. */
|
|
|
|
type: Type;
|
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
|
|
|
|
2018-04-03 23:56:48 +02:00
|
|
|
protected constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
type: Type,
|
|
|
|
declaration: VariableLikeDeclarationStatement | null
|
|
|
|
) {
|
|
|
|
super(program, simpleName, internalName);
|
|
|
|
this.type = type;
|
|
|
|
this.declaration = declaration;
|
|
|
|
}
|
|
|
|
|
2017-12-28 15:17:35 +01:00
|
|
|
withConstantIntegerValue(lo: i32, hi: i32): this {
|
2018-02-14 09:18:43 +01:00
|
|
|
this.constantValueKind = ConstantValueKind.INTEGER;
|
|
|
|
this.constantIntegerValue = i64_new(lo, hi);
|
2018-03-17 01:37:05 +01:00
|
|
|
this.set(CommonFlags.CONST | CommonFlags.INLINED);
|
2017-12-28 15:17:35 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
withConstantFloatValue(value: f64): this {
|
2018-02-14 09:18:43 +01:00
|
|
|
this.constantValueKind = ConstantValueKind.FLOAT;
|
2017-12-28 15:17:35 +01:00
|
|
|
this.constantFloatValue = value;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.set(CommonFlags.CONST | CommonFlags.INLINED);
|
2017-12-28 15:17:35 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** A global variable. */
|
|
|
|
export class Global extends VariableLikeElement {
|
|
|
|
|
|
|
|
kind = ElementKind.GLOBAL;
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
2018-04-03 23:56:48 +02:00
|
|
|
type: Type,
|
2018-04-24 23:11:11 +02:00
|
|
|
declaration: VariableLikeDeclarationStatement | null,
|
|
|
|
decoratorFlags: DecoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2018-04-03 23:56:48 +02:00
|
|
|
super(program, simpleName, internalName, type, declaration);
|
|
|
|
this.flags = declaration ? declaration.flags : CommonFlags.NONE;
|
2018-04-24 23:11:11 +02:00
|
|
|
this.decoratorFlags = decoratorFlags;
|
2018-01-04 06:00:42 +01:00
|
|
|
this.type = type; // resolved later if `void`
|
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
|
|
|
// not an Element on its own
|
|
|
|
|
|
|
|
/** Parameter name. */
|
2018-02-09 02:31:48 +01:00
|
|
|
name: string;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Parameter type. */
|
2017-10-19 18:55:27 +02:00
|
|
|
type: Type;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Parameter initializer. */
|
2017-11-20 23:39:50 +01:00
|
|
|
initializer: Expression | null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new function parameter. */
|
2018-02-09 02:31:48 +01:00
|
|
|
constructor(name: string, type: Type, initializer: Expression | null = null) {
|
2017-10-19 18:55:27 +02:00
|
|
|
this.name = name;
|
|
|
|
this.type = type;
|
2017-11-20 23:39:50 +01:00
|
|
|
this.initializer = initializer;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A function local. */
|
2017-12-28 15:17:35 +01:00
|
|
|
export class Local extends VariableLikeElement {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
|
|
|
kind = ElementKind.LOCAL;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Local index. */
|
2017-10-19 18:55:27 +02:00
|
|
|
index: i32;
|
2018-04-03 23:56:48 +02:00
|
|
|
/** Respective scoped global, if any. */
|
|
|
|
scopedGlobal: Global | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-04-03 23:56:48 +02:00
|
|
|
constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
index: i32,
|
|
|
|
type: Type,
|
|
|
|
declaration: VariableLikeDeclarationStatement | null = null
|
|
|
|
) {
|
|
|
|
super(program, simpleName, simpleName, type, declaration);
|
2017-10-19 18:55:27 +02:00
|
|
|
this.index = index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A yet unresolved function prototype. */
|
|
|
|
export class FunctionPrototype extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.FUNCTION_PROTOTYPE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: FunctionDeclaration;
|
2017-12-15 15:00:19 +01:00
|
|
|
/** If an instance method, the class prototype reference. */
|
2017-11-17 14:33:51 +01:00
|
|
|
classPrototype: ClassPrototype | null;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Resolved instances. */
|
2017-11-17 14:33:51 +01:00
|
|
|
instances: Map<string,Function> = new Map();
|
2018-03-12 14:06:39 +01:00
|
|
|
/** Class type arguments, if a partially resolved method of a generic class. Not set otherwise. */
|
2018-01-01 20:27:21 +01:00
|
|
|
classTypeArguments: Type[] | null = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** Operator kind, if an overload. */
|
|
|
|
operatorKind: OperatorKind = OperatorKind.INVALID;
|
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(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
declaration: FunctionDeclaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
classPrototype: ClassPrototype | null = null,
|
|
|
|
decoratorFlags: DecoratorFlags = DecoratorFlags.NONE
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2018-02-09 02:31:48 +01:00
|
|
|
this.declaration = declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.flags = declaration.flags;
|
|
|
|
this.classPrototype = classPrototype;
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = decoratorFlags;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves this prototype to an instance using the specified concrete type arguments. */
|
2018-02-25 00:13:39 +01:00
|
|
|
resolve(
|
|
|
|
functionTypeArguments: Type[] | null = null,
|
|
|
|
contextualTypeArguments: Map<string,Type> | null = null
|
|
|
|
): Function | null {
|
2018-01-01 20:27:21 +01:00
|
|
|
var instanceKey = functionTypeArguments ? typesToString(functionTypeArguments) : "";
|
2017-12-28 04:09:40 +01:00
|
|
|
var instance = this.instances.get(instanceKey);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (instance) return instance;
|
2018-01-02 03:54:06 +01:00
|
|
|
|
2017-12-28 04:09:40 +01:00
|
|
|
var declaration = this.declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
var isInstance = this.is(CommonFlags.INSTANCE);
|
2018-03-20 12:02:05 +01:00
|
|
|
var classPrototype = this.classPrototype;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// inherit contextual type arguments as provided. might be overridden.
|
2018-01-01 20:27:21 +01:00
|
|
|
var inheritedTypeArguments = contextualTypeArguments;
|
|
|
|
contextualTypeArguments = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
if (inheritedTypeArguments) {
|
2018-03-12 14:06:39 +01:00
|
|
|
for (let [inheritedName, inheritedType] of inheritedTypeArguments) {
|
|
|
|
contextualTypeArguments.set(
|
|
|
|
inheritedName,
|
|
|
|
inheritedType
|
|
|
|
);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
// override with class type arguments if a partially resolved instance method
|
|
|
|
var classTypeArguments = this.classTypeArguments;
|
|
|
|
if (classTypeArguments) { // set only if partially resolved
|
2018-03-20 12:02:05 +01:00
|
|
|
assert(this.is(CommonFlags.INSTANCE));
|
|
|
|
let classDeclaration = assert(classPrototype).declaration;
|
2018-03-12 14:06:39 +01:00
|
|
|
let classTypeParameters = classDeclaration.typeParameters;
|
|
|
|
let numClassTypeParameters = classTypeParameters.length;
|
|
|
|
assert(numClassTypeParameters == classTypeArguments.length);
|
|
|
|
for (let i = 0; i < numClassTypeParameters; ++i) {
|
|
|
|
contextualTypeArguments.set(
|
|
|
|
classTypeParameters[i].name.text,
|
|
|
|
classTypeArguments[i]
|
|
|
|
);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-12 14:06:39 +01:00
|
|
|
} else {
|
|
|
|
assert(!classTypeArguments);
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
// override with function specific type arguments
|
|
|
|
var signatureNode = declaration.signature;
|
2018-01-01 20:27:21 +01:00
|
|
|
var functionTypeParameters = declaration.typeParameters;
|
2018-03-12 14:06:39 +01:00
|
|
|
var numFunctionTypeArguments: i32;
|
|
|
|
if (functionTypeArguments && (numFunctionTypeArguments = functionTypeArguments.length)) {
|
|
|
|
assert(functionTypeParameters && numFunctionTypeArguments == functionTypeParameters.length);
|
|
|
|
for (let i = 0; i < numFunctionTypeArguments; ++i) {
|
|
|
|
contextualTypeArguments.set(
|
|
|
|
(<TypeParameterNode[]>functionTypeParameters)[i].name.text,
|
|
|
|
functionTypeArguments[i]
|
|
|
|
);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-12 14:06:39 +01:00
|
|
|
} else {
|
|
|
|
assert(!functionTypeParameters || functionTypeParameters.length == 0);
|
2017-12-27 02:37:53 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
// resolve class if an instance method
|
2018-01-01 20:27:21 +01:00
|
|
|
var classInstance: Class | null = null;
|
2018-03-12 14:06:39 +01:00
|
|
|
var thisType: Type | null = null;
|
|
|
|
if (isInstance) {
|
2018-03-20 12:02:05 +01:00
|
|
|
classInstance = assert(classPrototype).resolve(classTypeArguments, contextualTypeArguments); // reports
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!classInstance) return null;
|
2018-04-17 02:50:38 +02:00
|
|
|
thisType = classInstance.type;
|
2018-04-11 23:35:19 +02:00
|
|
|
contextualTypeArguments.set("this", thisType);
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// resolve signature node
|
2018-05-30 16:22:56 +02:00
|
|
|
var signatureParameters = signatureNode.parameters;
|
2018-03-12 14:06:39 +01:00
|
|
|
var signatureParameterCount = signatureParameters.length;
|
|
|
|
var parameterTypes = new Array<Type>(signatureParameterCount);
|
|
|
|
var parameterNames = new Array<string>(signatureParameterCount);
|
|
|
|
var requiredParameters = 0;
|
|
|
|
for (let i = 0; i < signatureParameterCount; ++i) {
|
|
|
|
let parameterDeclaration = signatureParameters[i];
|
|
|
|
if (parameterDeclaration.parameterKind == ParameterKind.DEFAULT) {
|
|
|
|
requiredParameters = i + 1;
|
|
|
|
}
|
|
|
|
let typeNode = assert(parameterDeclaration.type);
|
|
|
|
let parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
|
|
|
|
if (!parameterType) return null;
|
|
|
|
parameterTypes[i] = parameterType;
|
|
|
|
parameterNames[i] = parameterDeclaration.name.text;
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
2018-01-28 15:13:31 +01:00
|
|
|
|
|
|
|
var returnType: Type;
|
2018-03-23 01:47:01 +01:00
|
|
|
if (this.is(CommonFlags.SET)) {
|
2018-01-28 15:13:31 +01:00
|
|
|
returnType = Type.void; // not annotated
|
2018-03-23 01:47:01 +01:00
|
|
|
} else if (this.is(CommonFlags.CONSTRUCTOR)) {
|
|
|
|
returnType = assert(classInstance).type; // not annotated
|
2018-01-28 15:13:31 +01:00
|
|
|
} else {
|
2018-03-12 14:06:39 +01:00
|
|
|
let typeNode = assert(signatureNode.returnType);
|
|
|
|
let type = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
|
|
|
|
if (!type) return null;
|
|
|
|
returnType = type;
|
2018-01-28 15:13:31 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 14:06:39 +01:00
|
|
|
var signature = new Signature(parameterTypes, returnType, thisType);
|
|
|
|
signature.parameterNames = parameterNames;
|
|
|
|
signature.requiredParameters = requiredParameters;
|
|
|
|
|
|
|
|
var internalName = this.internalName;
|
|
|
|
if (instanceKey.length) internalName += "<" + instanceKey + ">";
|
2018-04-11 23:35:19 +02:00
|
|
|
instance = new Function(
|
|
|
|
this,
|
|
|
|
internalName,
|
|
|
|
signature,
|
|
|
|
classInstance
|
|
|
|
? classInstance
|
|
|
|
: classPrototype,
|
|
|
|
contextualTypeArguments
|
|
|
|
);
|
2017-10-19 18:55:27 +02:00
|
|
|
this.instances.set(instanceKey, instance);
|
2018-05-06 00:00:54 +02:00
|
|
|
this.program.instancesLookup.set(internalName, instance);
|
2017-10-19 18:55:27 +02:00
|
|
|
return instance;
|
|
|
|
}
|
2017-11-20 23:39:50 +01:00
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves this prototype partially by applying the specified inherited class type arguments. */
|
|
|
|
resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null {
|
2018-03-20 12:02:05 +01:00
|
|
|
assert(this.is(CommonFlags.INSTANCE));
|
2018-04-07 03:27:22 +02:00
|
|
|
var classPrototype = assert(this.classPrototype);
|
|
|
|
|
|
|
|
if (!(classTypeArguments && classTypeArguments.length)) return this; // no need to clone
|
|
|
|
|
|
|
|
var simpleName = this.simpleName;
|
|
|
|
var partialKey = typesToString(classTypeArguments);
|
|
|
|
var partialPrototype = new FunctionPrototype(
|
|
|
|
this.program,
|
|
|
|
simpleName,
|
|
|
|
classPrototype.internalName + "<" + partialKey + ">" + INSTANCE_DELIMITER + simpleName,
|
|
|
|
this.declaration,
|
2018-04-11 23:35:19 +02:00
|
|
|
classPrototype,
|
|
|
|
this.decoratorFlags
|
2018-04-07 03:27:22 +02:00
|
|
|
);
|
|
|
|
partialPrototype.flags = this.flags;
|
2018-04-11 23:35:19 +02:00
|
|
|
partialPrototype.operatorKind = this.operatorKind;
|
2018-04-07 03:27:22 +02:00
|
|
|
partialPrototype.classTypeArguments = classTypeArguments;
|
|
|
|
return partialPrototype;
|
2018-03-03 18:38:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Resolves the specified type arguments prior to resolving this prototype to an instance. */
|
|
|
|
resolveUsingTypeArguments(
|
2018-03-12 14:06:39 +01:00
|
|
|
typeArgumentNodes: CommonTypeNode[] | null,
|
2018-02-25 00:13:39 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null,
|
|
|
|
reportNode: Node
|
|
|
|
): Function | null {
|
2018-01-01 20:27:21 +01:00
|
|
|
var resolvedTypeArguments: Type[] | null = null;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.is(CommonFlags.GENERIC)) {
|
2017-12-24 03:19:47 +01:00
|
|
|
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
|
2018-04-12 20:40:00 +02:00
|
|
|
resolvedTypeArguments = this.program.resolveTypeArguments( // reports
|
2018-02-28 01:48:01 +01:00
|
|
|
assert(this.declaration.typeParameters),
|
2018-02-25 00:13:39 +01:00
|
|
|
typeArgumentNodes,
|
|
|
|
contextualTypeArguments,
|
|
|
|
reportNode
|
|
|
|
);
|
|
|
|
if (!resolvedTypeArguments) return null;
|
2017-11-20 23:39:50 +01:00
|
|
|
}
|
|
|
|
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves the type arguments to use when compiling a built-in call. Must be a built-in. */
|
|
|
|
resolveBuiltinTypeArguments(
|
2018-03-12 14:06:39 +01:00
|
|
|
typeArgumentNodes: CommonTypeNode[] | null,
|
2018-03-03 18:38:38 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null
|
|
|
|
): Type[] | null {
|
2018-03-17 01:37:05 +01:00
|
|
|
assert(this.is(CommonFlags.BUILTIN));
|
2018-03-03 18:38:38 +01:00
|
|
|
var resolvedTypeArguments: Type[] | null = null;
|
|
|
|
if (typeArgumentNodes) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let k = typeArgumentNodes.length;
|
2018-03-03 18:38:38 +01:00
|
|
|
resolvedTypeArguments = new Array<Type>(k);
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let i = 0; i < k; ++i) {
|
|
|
|
let resolvedType = this.program.resolveType( // reports
|
2018-03-03 18:38:38 +01:00
|
|
|
typeArgumentNodes[i],
|
|
|
|
contextualTypeArguments,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
if (!resolvedType) return null;
|
|
|
|
resolvedTypeArguments[i] = resolvedType;
|
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
2018-03-03 18:38:38 +01:00
|
|
|
return resolvedTypeArguments;
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
|
|
|
|
2017-12-15 15:00:19 +01:00
|
|
|
toString(): string { return this.simpleName; }
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved function. */
|
|
|
|
export class Function extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.FUNCTION;
|
2017-11-20 23:39:50 +01:00
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Prototype reference. */
|
|
|
|
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[] = [];
|
|
|
|
/** Current break context label. */
|
2017-10-19 18:55:27 +02:00
|
|
|
breakContext: string | null = null;
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Contextual type arguments. */
|
2017-12-15 15:00:19 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null;
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Current control flow. */
|
|
|
|
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;
|
2018-04-03 23:56:48 +02:00
|
|
|
/** The outer scope, if a function expression. */
|
|
|
|
outerScope: Flow | null = null;
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-12-08 16:11:58 +01:00
|
|
|
private nextBreakId: i32 = 0;
|
|
|
|
private breakStack: i32[] | null = null;
|
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(
|
|
|
|
prototype: FunctionPrototype,
|
|
|
|
internalName: string,
|
2018-03-12 14:06:39 +01:00
|
|
|
signature: Signature,
|
2018-04-15 00:34:19 +02:00
|
|
|
parent: Element | null = null,
|
2018-04-11 23:35:19 +02:00
|
|
|
contextualTypeArguments: Map<string,Type> | null = null
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(prototype.program, prototype.simpleName, internalName);
|
2017-12-13 04:46:05 +01:00
|
|
|
this.prototype = prototype;
|
2018-03-12 14:06:39 +01:00
|
|
|
this.signature = signature;
|
2018-04-15 00:34:19 +02:00
|
|
|
this.parent = parent;
|
2017-12-13 04:46:05 +01:00
|
|
|
this.flags = prototype.flags;
|
2018-04-11 23:35:19 +02:00
|
|
|
this.decoratorFlags = prototype.decoratorFlags;
|
|
|
|
this.contextualTypeArguments = contextualTypeArguments;
|
2018-03-24 18:39:20 +01:00
|
|
|
if (!(prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
|
2018-03-12 14:06:39 +01:00
|
|
|
let localIndex = 0;
|
2018-04-15 00:34:19 +02:00
|
|
|
if (parent && parent.kind == ElementKind.CLASS) {
|
2018-03-17 01:37:05 +01:00
|
|
|
assert(this.is(CommonFlags.INSTANCE));
|
2018-04-25 05:04:35 +02:00
|
|
|
let local = new Local(
|
|
|
|
prototype.program,
|
2018-03-12 14:06:39 +01:00
|
|
|
"this",
|
2018-04-25 05:04:35 +02:00
|
|
|
localIndex++,
|
|
|
|
assert(signature.thisType)
|
2018-03-12 14:06:39 +01:00
|
|
|
);
|
2018-04-25 05:04:35 +02:00
|
|
|
this.localsByName.set("this", local);
|
|
|
|
this.localsByIndex[local.index] = local;
|
2018-04-15 00:34:19 +02:00
|
|
|
let inheritedTypeArguments = (<Class>parent).contextualTypeArguments;
|
2018-04-11 23:35:19 +02:00
|
|
|
if (inheritedTypeArguments) {
|
|
|
|
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
|
|
|
|
for (let [inheritedName, inheritedType] of inheritedTypeArguments) {
|
|
|
|
if (!this.contextualTypeArguments.has(inheritedName)) {
|
|
|
|
this.contextualTypeArguments.set(inheritedName, inheritedType);
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-02-02 03:07:54 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else {
|
2018-03-17 01:37:05 +01:00
|
|
|
assert(!this.is(CommonFlags.INSTANCE)); // internal error
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-12 14:06:39 +01:00
|
|
|
let parameterTypes = signature.parameterTypes;
|
|
|
|
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
|
|
|
|
let parameterType = parameterTypes[i];
|
|
|
|
let parameterName = signature.getParameterName(i);
|
2018-04-25 05:04:35 +02:00
|
|
|
let local = new Local(
|
|
|
|
prototype.program,
|
2018-02-25 00:13:39 +01:00
|
|
|
parameterName,
|
2018-04-25 05:04:35 +02:00
|
|
|
localIndex++,
|
|
|
|
parameterType
|
|
|
|
// FIXME: declaration?
|
2018-02-25 00:13:39 +01:00
|
|
|
);
|
2018-04-25 05:04:35 +02: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);
|
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;
|
2018-02-25 00:13:39 +01:00
|
|
|
var local = new Local(
|
|
|
|
this.prototype.program,
|
|
|
|
name
|
|
|
|
? name
|
|
|
|
: "var$" + localIndex.toString(10),
|
|
|
|
localIndex,
|
2018-04-03 23:56:48 +02:00
|
|
|
type,
|
|
|
|
declaration
|
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;
|
|
|
|
}
|
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
private tempI32s: Local[] | null = null;
|
|
|
|
private tempI64s: Local[] | null = null;
|
|
|
|
private tempF32s: Local[] | null = null;
|
|
|
|
private tempF64s: Local[] | null = null;
|
2017-12-12 09:32:03 +01:00
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
/** Gets a free temporary local of the specified type. */
|
2018-05-08 22:16:12 +02:00
|
|
|
getTempLocal(type: Type, wrapped: bool = false): Local {
|
2017-12-28 04:09:40 +01:00
|
|
|
var temps: Local[] | null;
|
2017-12-24 03:19:47 +01:00
|
|
|
switch (type.toNativeType()) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NativeType.I32: {
|
|
|
|
temps = this.tempI32s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.I64: {
|
|
|
|
temps = this.tempI64s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F32: {
|
|
|
|
temps = this.tempF32s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F64: {
|
|
|
|
temps = this.tempF64s;
|
|
|
|
break;
|
|
|
|
}
|
2018-01-02 03:54:06 +01:00
|
|
|
default: throw new Error("concrete type expected");
|
2017-12-12 09:32:03 +01:00
|
|
|
}
|
2018-05-06 00:00:54 +02:00
|
|
|
var local: Local;
|
2018-02-14 19:21:31 +01:00
|
|
|
if (temps && temps.length) {
|
2018-05-06 00:00:54 +02:00
|
|
|
local = temps.pop();
|
|
|
|
local.type = type;
|
2018-05-11 16:31:56 +02:00
|
|
|
local.flags = CommonFlags.NONE;
|
2018-05-06 00:00:54 +02:00
|
|
|
} else {
|
|
|
|
local = this.addLocal(type);
|
2018-02-14 19:21:31 +01:00
|
|
|
}
|
2018-05-08 22:16:12 +02:00
|
|
|
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
|
|
|
|
this.flow.setLocalWrapped(local.index, wrapped);
|
|
|
|
}
|
2018-05-06 00:00:54 +02:00
|
|
|
return local;
|
2017-12-12 09:32:03 +01:00
|
|
|
}
|
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
/** Frees the temporary local for reuse. */
|
2017-12-12 09:32:03 +01:00
|
|
|
freeTempLocal(local: Local): void {
|
2018-04-11 23:35:19 +02:00
|
|
|
if (local.is(CommonFlags.INLINED)) return;
|
|
|
|
assert(local.index >= 0);
|
2017-12-28 04:09:40 +01:00
|
|
|
var temps: Local[];
|
2018-01-02 03:54:06 +01:00
|
|
|
assert(local.type != null); // internal error
|
2017-12-28 15:20:37 +01:00
|
|
|
switch ((<Type>local.type).toNativeType()) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NativeType.I32: {
|
|
|
|
temps = this.tempI32s || (this.tempI32s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.I64: {
|
|
|
|
temps = this.tempI64s || (this.tempI64s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F32: {
|
|
|
|
temps = this.tempF32s || (this.tempF32s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F64: {
|
|
|
|
temps = this.tempF64s || (this.tempF64s = []);
|
|
|
|
break;
|
|
|
|
}
|
2018-01-02 03:54:06 +01:00
|
|
|
default: throw new Error("concrete type expected");
|
2017-12-12 09:32:03 +01:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
assert(local.index >= 0);
|
2017-12-12 09:32:03 +01:00
|
|
|
temps.push(local);
|
|
|
|
}
|
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
/** Gets and immediately frees a temporary local of the specified type. */
|
2018-05-06 00:00:54 +02:00
|
|
|
getAndFreeTempLocal(type: Type, wrapped: bool): Local {
|
2017-12-28 04:09:40 +01:00
|
|
|
var temps: Local[];
|
2017-12-24 03:19:47 +01:00
|
|
|
switch (type.toNativeType()) {
|
2018-03-12 14:06:39 +01:00
|
|
|
case NativeType.I32: {
|
|
|
|
temps = this.tempI32s || (this.tempI32s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.I64: {
|
|
|
|
temps = this.tempI64s || (this.tempI64s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F32: {
|
|
|
|
temps = this.tempF32s || (this.tempF32s = []);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NativeType.F64: {
|
|
|
|
temps = this.tempF64s || (this.tempF64s = []);
|
|
|
|
break;
|
|
|
|
}
|
2018-01-02 03:54:06 +01:00
|
|
|
default: throw new Error("concrete type expected");
|
2017-12-12 09:32:03 +01:00
|
|
|
}
|
2018-05-06 00:00:54 +02:00
|
|
|
var local: Local;
|
|
|
|
if (temps.length) {
|
|
|
|
local = temps[temps.length - 1];
|
|
|
|
local.type = type;
|
|
|
|
} else {
|
|
|
|
local = this.addLocal(type);
|
|
|
|
temps.push(local);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-05-08 22:16:12 +02:00
|
|
|
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
|
|
|
|
this.flow.setLocalWrapped(local.index, wrapped);
|
|
|
|
}
|
2017-12-12 09:32:03 +01:00
|
|
|
return local;
|
|
|
|
}
|
|
|
|
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Enters a(nother) break context. */
|
2017-10-19 18:55:27 +02:00
|
|
|
enterBreakContext(): string {
|
2017-12-28 04:09:40 +01:00
|
|
|
var id = this.nextBreakId++;
|
2018-05-06 05:46:35 +02:00
|
|
|
if (!this.breakStack) this.breakStack = [ id ];
|
|
|
|
else this.breakStack.push(id);
|
2017-12-08 16:11:58 +01:00
|
|
|
return this.breakContext = id.toString(10);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-20 23:39:50 +01:00
|
|
|
/** Leaves the current break context. */
|
2017-10-19 18:55:27 +02:00
|
|
|
leaveBreakContext(): void {
|
2017-12-08 16:11:58 +01:00
|
|
|
assert(this.breakStack != null);
|
2017-12-28 04:09:40 +01:00
|
|
|
var length = (<i32[]>this.breakStack).length;
|
2017-12-08 16:11:58 +01:00
|
|
|
assert(length > 0);
|
|
|
|
(<i32[]>this.breakStack).pop();
|
|
|
|
if (length > 1) {
|
2017-12-24 03:19:47 +01:00
|
|
|
this.breakContext = (<i32[]>this.breakStack)[length - 2].toString(10);
|
2017-12-08 16:11:58 +01:00
|
|
|
} else {
|
2017-10-19 18:55:27 +02:00
|
|
|
this.breakContext = null;
|
2017-12-08 16:11:58 +01:00
|
|
|
this.breakStack = null;
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
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;
|
|
|
|
this.breakContext = null;
|
|
|
|
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-12-16 17:54:53 +01:00
|
|
|
/** Returns the TypeScript representation of this function. */
|
2017-12-15 15:00:19 +01:00
|
|
|
toString(): string { return this.prototype.simpleName; }
|
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 {
|
|
|
|
|
|
|
|
kind = ElementKind.FUNCTION_TARGET;
|
|
|
|
|
|
|
|
/** Underlying signature. */
|
|
|
|
signature: Signature;
|
|
|
|
/** Function type. */
|
|
|
|
type: Type;
|
|
|
|
|
|
|
|
/** Constructs a new function target. */
|
|
|
|
constructor(program: Program, signature: Signature) {
|
|
|
|
super(program, "", "");
|
|
|
|
var simpleName = signature.toSignatureString();
|
|
|
|
this.simpleName = simpleName;
|
|
|
|
this.internalName = simpleName;
|
|
|
|
this.signature = signature;
|
|
|
|
this.type = Type.u32.asFunction(signature);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A yet unresolved instance field prototype. */
|
2018-01-17 02:08:14 +01:00
|
|
|
export class FieldPrototype extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.FIELD_PROTOTYPE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
2018-01-17 02:08:14 +01:00
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: FieldDeclaration;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Parent class prototype. */
|
2017-11-17 14:33:51 +01:00
|
|
|
classPrototype: ClassPrototype;
|
|
|
|
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Constructs a new field prototype. */
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
classPrototype: ClassPrototype,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
declaration: FieldDeclaration
|
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(classPrototype.program, simpleName, internalName);
|
2017-11-17 14:33:51 +01:00
|
|
|
this.classPrototype = classPrototype;
|
2018-02-09 02:31:48 +01:00
|
|
|
this.declaration = declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.flags = declaration.flags;
|
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
|
|
|
|
|
|
|
kind = ElementKind.FIELD;
|
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(
|
|
|
|
prototype: FieldPrototype,
|
|
|
|
internalName: string,
|
|
|
|
type: Type,
|
2018-04-15 00:34:19 +02:00
|
|
|
declaration: FieldDeclaration,
|
|
|
|
parent: Class
|
2018-04-03 23:56:48 +02:00
|
|
|
) {
|
|
|
|
super(prototype.program, prototype.simpleName, internalName, type, declaration);
|
2018-01-03 18:33:27 +01:00
|
|
|
this.prototype = prototype;
|
2017-12-13 04:46:05 +01:00
|
|
|
this.flags = prototype.flags;
|
2017-11-17 14:33:51 +01:00
|
|
|
this.type = type;
|
2018-04-15 00:34:19 +02:00
|
|
|
this.parent = parent;
|
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. */
|
|
|
|
export class Property extends Element {
|
2017-12-16 17:54:53 +01:00
|
|
|
|
2017-12-27 02:37:53 +01:00
|
|
|
kind = ElementKind.PROPERTY;
|
2017-12-16 17:54:53 +01:00
|
|
|
|
|
|
|
/** Parent class prototype. */
|
2017-12-27 02:37:53 +01:00
|
|
|
parent: ClassPrototype;
|
|
|
|
/** 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(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
|
|
|
parent: ClassPrototype
|
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2017-12-27 02:37:53 +01:00
|
|
|
this.parent = parent;
|
2017-12-16 17:54:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A yet unresolved class prototype. */
|
2017-12-13 23:24:13 +01:00
|
|
|
export class ClassPrototype extends Element {
|
2017-11-17 14:33:51 +01:00
|
|
|
|
|
|
|
kind = ElementKind.CLASS_PROTOTYPE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: ClassDeclaration;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Resolved instances. */
|
2017-12-13 23:24:13 +01:00
|
|
|
instances: Map<string,Class> = new Map();
|
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();
|
2018-01-06 10:20:38 +01:00
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
constructor(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration: ClassDeclaration,
|
|
|
|
decoratorFlags: DecoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2017-12-27 22:38:32 +01:00
|
|
|
super(program, simpleName, internalName);
|
2018-02-09 02:31:48 +01:00
|
|
|
this.declaration = declaration;
|
2018-03-17 01:37:05 +01:00
|
|
|
this.flags = declaration.flags;
|
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
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves this prototype to an instance using the specified concrete type arguments. */
|
|
|
|
resolve(
|
|
|
|
typeArguments: Type[] | null,
|
|
|
|
contextualTypeArguments: Map<string,Type> | null = null
|
|
|
|
): Class | null {
|
2018-01-01 20:27:21 +01:00
|
|
|
var instanceKey = typeArguments ? typesToString(typeArguments) : "";
|
2017-12-28 04:09:40 +01:00
|
|
|
var instance = this.instances.get(instanceKey);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (instance) return instance;
|
2018-01-01 20:27:21 +01:00
|
|
|
|
|
|
|
// inherit contextual type arguments
|
|
|
|
var inheritedTypeArguments = contextualTypeArguments;
|
|
|
|
contextualTypeArguments = new Map();
|
2018-02-25 00:13:39 +01:00
|
|
|
if (inheritedTypeArguments) {
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let [inheritedName, inheritedType] of inheritedTypeArguments) {
|
2018-01-01 20:27:21 +01:00
|
|
|
contextualTypeArguments.set(inheritedName, inheritedType);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
|
2018-02-09 02:31:48 +01:00
|
|
|
var declaration = this.declaration;
|
2018-01-04 01:36:26 +01:00
|
|
|
var baseClass: Class | null = null;
|
|
|
|
if (declaration.extendsType) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let baseClassType = this.program.resolveType(declaration.extendsType, null); // reports
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!baseClassType) return null;
|
2018-03-21 23:27:53 +01:00
|
|
|
if (!(baseClass = baseClassType.classReference)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.program.error(
|
|
|
|
DiagnosticCode.A_class_may_only_extend_another_class,
|
|
|
|
declaration.extendsType.range
|
|
|
|
);
|
2018-01-04 01:36:26 +01:00
|
|
|
return null;
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
if (baseClass.hasDecorator(DecoratorFlags.SEALED)) {
|
2018-03-23 12:45:29 +01:00
|
|
|
this.program.error(
|
|
|
|
DiagnosticCode.Class_0_is_sealed_and_cannot_be_extended,
|
|
|
|
declaration.extendsType.range, baseClass.internalName
|
|
|
|
);
|
|
|
|
return null;
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
if (baseClass.hasDecorator(DecoratorFlags.UNMANAGED) != this.hasDecorator(DecoratorFlags.UNMANAGED)) {
|
2018-02-25 00:13:39 +01:00
|
|
|
this.program.error(
|
2018-04-11 23:35:19 +02:00
|
|
|
DiagnosticCode.Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa,
|
2018-02-25 00:13:39 +01:00
|
|
|
Range.join(declaration.name.range, declaration.extendsType.range)
|
|
|
|
);
|
2018-01-04 01:36:26 +01:00
|
|
|
return null;
|
|
|
|
}
|
2018-01-28 15:13:31 +01:00
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
|
|
|
|
// override call specific contextual type arguments if provided
|
|
|
|
var i: i32, k: i32;
|
2018-01-02 03:54:06 +01:00
|
|
|
if (typeArguments) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if ((k = typeArguments.length) != declaration.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
|
|
|
}
|
|
|
|
for (i = 0; i < k; ++i) {
|
2018-02-14 19:21:31 +01:00
|
|
|
contextualTypeArguments.set(declaration.typeParameters[i].name.text, typeArguments[i]);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
|
|
|
} else if (declaration.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
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
|
2018-03-31 18:18:55 +02:00
|
|
|
var simpleName = this.simpleName;
|
2017-12-28 04:09:40 +01:00
|
|
|
var internalName = this.internalName;
|
2018-02-25 00:13:39 +01:00
|
|
|
if (instanceKey.length) {
|
2018-03-31 18:18:55 +02:00
|
|
|
simpleName += "<" + instanceKey + ">";
|
2017-12-15 15:00:19 +01:00
|
|
|
internalName += "<" + instanceKey + ">";
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-03-31 18:18:55 +02:00
|
|
|
instance = new Class(this, simpleName, internalName, typeArguments, baseClass);
|
2017-12-15 15:00:19 +01:00
|
|
|
instance.contextualTypeArguments = contextualTypeArguments;
|
|
|
|
this.instances.set(instanceKey, instance);
|
2018-05-06 00:00:54 +02:00
|
|
|
this.program.instancesLookup.set(internalName, instance);
|
2018-01-01 20:27:21 +01:00
|
|
|
|
2018-01-13 23:38:07 +01:00
|
|
|
var memoryOffset: u32 = 0;
|
2018-01-04 01:36:26 +01:00
|
|
|
if (baseClass) {
|
2018-01-13 23:38:07 +01:00
|
|
|
memoryOffset = baseClass.currentMemoryOffset;
|
2018-01-04 01:36:26 +01:00
|
|
|
if (baseClass.members) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!instance.members) instance.members = new Map();
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let inheritedMember of baseClass.members.values()) {
|
2018-01-04 01:36:26 +01:00
|
|
|
instance.members.set(inheritedMember.simpleName, inheritedMember);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-04 01:36:26 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// Resolve constructor
|
2018-01-28 15:13:31 +01:00
|
|
|
if (this.constructorPrototype) {
|
2018-03-13 02:32:10 +01:00
|
|
|
let partialConstructor = this.constructorPrototype.resolvePartial(typeArguments); // reports
|
2018-04-07 03:27:22 +02:00
|
|
|
if (partialConstructor) instance.constructorInstance = partialConstructor.resolve(); // reports
|
2018-01-28 15:13:31 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 23:35:19 +02:00
|
|
|
// Resolve instance members
|
2018-02-25 00:13:39 +01:00
|
|
|
if (this.instanceMembers) {
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let member of this.instanceMembers.values()) {
|
2018-01-01 20:27:21 +01:00
|
|
|
switch (member.kind) {
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
// Lay out fields in advance
|
|
|
|
case ElementKind.FIELD_PROTOTYPE: {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!instance.members) instance.members = new Map();
|
2018-03-13 02:32:10 +01:00
|
|
|
let fieldDeclaration = (<FieldPrototype>member).declaration;
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!fieldDeclaration.type) {
|
|
|
|
throw new Error("type expected"); // TODO: check if parent class defines a type
|
|
|
|
}
|
2018-03-13 02:32:10 +01:00
|
|
|
let fieldType = this.program.resolveType( // reports
|
2018-02-25 00:13:39 +01:00
|
|
|
fieldDeclaration.type,
|
|
|
|
instance.contextualTypeArguments
|
|
|
|
);
|
2018-01-01 20:27:21 +01:00
|
|
|
if (fieldType) {
|
2018-03-20 23:41:37 +01:00
|
|
|
let fieldInstance = new Field(
|
|
|
|
<FieldPrototype>member,
|
|
|
|
internalName + INSTANCE_DELIMITER + (<FieldPrototype>member).simpleName,
|
2018-04-03 23:56:48 +02:00
|
|
|
fieldType,
|
2018-04-15 00:34:19 +02:00
|
|
|
fieldDeclaration,
|
|
|
|
instance
|
2018-03-20 23:41:37 +01:00
|
|
|
);
|
2018-01-13 23:38:07 +01:00
|
|
|
switch (fieldType.byteSize) { // align
|
2018-01-01 20:27:21 +01:00
|
|
|
case 1: break;
|
2018-03-12 14:06:39 +01:00
|
|
|
case 2: {
|
|
|
|
if (memoryOffset & 1) ++memoryOffset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4: {
|
|
|
|
if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 8: {
|
|
|
|
if (memoryOffset & 7) memoryOffset = (memoryOffset | 7) + 1;
|
|
|
|
break;
|
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
default: assert(false);
|
|
|
|
}
|
|
|
|
fieldInstance.memoryOffset = memoryOffset;
|
|
|
|
memoryOffset += fieldType.byteSize;
|
|
|
|
instance.members.set(member.simpleName, fieldInstance);
|
|
|
|
}
|
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
// Partially resolve methods as these might have type arguments on their own
|
|
|
|
case ElementKind.FUNCTION_PROTOTYPE: {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!instance.members) instance.members = new Map();
|
2018-04-11 23:35:19 +02:00
|
|
|
let partialPrototype = (<FunctionPrototype>member).resolvePartial(typeArguments); // reports
|
|
|
|
if (partialPrototype) {
|
|
|
|
partialPrototype.internalName = internalName + INSTANCE_DELIMITER + partialPrototype.simpleName;
|
|
|
|
instance.members.set(member.simpleName, partialPrototype);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
// Clone properties and partially resolve the wrapped accessors for consistence with other methods
|
|
|
|
case ElementKind.PROPERTY: {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!instance.members) instance.members = new Map();
|
2018-03-20 23:41:37 +01:00
|
|
|
let getterPrototype = assert((<Property>member).getterPrototype);
|
|
|
|
let setterPrototype = (<Property>member).setterPrototype;
|
|
|
|
let instanceProperty = new Property(
|
|
|
|
this.program,
|
|
|
|
member.simpleName,
|
|
|
|
internalName + INSTANCE_DELIMITER + member.simpleName,
|
|
|
|
this
|
|
|
|
);
|
2018-03-21 01:16:46 +01:00
|
|
|
let partialGetterPrototype = getterPrototype.resolvePartial(typeArguments);
|
2018-03-20 23:41:37 +01:00
|
|
|
if (!partialGetterPrototype) return null;
|
|
|
|
partialGetterPrototype.internalName = (
|
|
|
|
internalName + INSTANCE_DELIMITER + partialGetterPrototype.simpleName
|
|
|
|
);
|
|
|
|
instanceProperty.getterPrototype = partialGetterPrototype;
|
|
|
|
if (setterPrototype) {
|
2018-03-21 01:16:46 +01:00
|
|
|
let partialSetterPrototype = setterPrototype.resolvePartial(typeArguments);
|
2018-03-20 23:41:37 +01:00
|
|
|
if (!partialSetterPrototype) return null;
|
|
|
|
partialSetterPrototype.internalName = (
|
|
|
|
internalName + INSTANCE_DELIMITER + partialSetterPrototype.simpleName
|
|
|
|
);
|
|
|
|
instanceProperty.setterPrototype = partialSetterPrototype;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-14 21:17:43 +01:00
|
|
|
instance.members.set(member.simpleName, instanceProperty);
|
2018-01-05 18:19:32 +01:00
|
|
|
break;
|
2018-03-12 14:06:39 +01:00
|
|
|
}
|
2018-03-21 01:16:46 +01:00
|
|
|
default: assert(false);
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
|
|
|
|
// Fully resolve operator overloads (don't have type parameters on their own)
|
|
|
|
for (let [kind, prototype] of this.overloadPrototypes) {
|
|
|
|
assert(kind != OperatorKind.INVALID);
|
|
|
|
let operatorInstance: Function | null;
|
|
|
|
if (prototype.is(CommonFlags.INSTANCE)) {
|
|
|
|
let operatorPartial = prototype.resolvePartial(typeArguments); // reports
|
|
|
|
if (!operatorPartial) continue;
|
|
|
|
operatorInstance = operatorPartial.resolve(); // reports
|
|
|
|
} else {
|
|
|
|
operatorInstance = prototype.resolve(); // reports
|
|
|
|
}
|
|
|
|
if (!operatorInstance) continue;
|
|
|
|
let overloads = instance.overloads;
|
|
|
|
if (!overloads) instance.overloads = overloads = new Map();
|
|
|
|
overloads.set(kind, operatorInstance);
|
|
|
|
}
|
|
|
|
|
2018-03-24 00:38:49 +01:00
|
|
|
instance.currentMemoryOffset = memoryOffset; // offsetof<this>() is the class' byte size in memory
|
2017-12-15 15:00:19 +01:00
|
|
|
return instance;
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
2017-11-20 23:39:50 +01:00
|
|
|
|
2018-03-03 18:38:38 +01:00
|
|
|
/** Resolves the specified type arguments prior to resolving this prototype to an instance. */
|
|
|
|
resolveUsingTypeArguments(
|
2018-03-12 14:06:39 +01:00
|
|
|
typeArgumentNodes: CommonTypeNode[] | null,
|
2018-02-25 00:13:39 +01:00
|
|
|
contextualTypeArguments: Map<string,Type> | null,
|
|
|
|
alternativeReportNode: Node | null
|
|
|
|
): Class | null {
|
2018-01-02 03:54:06 +01:00
|
|
|
var resolvedTypeArguments: Type[] | null = null;
|
2018-03-17 01:37:05 +01:00
|
|
|
if (this.is(CommonFlags.GENERIC)) {
|
2017-12-15 15:00:19 +01:00
|
|
|
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
|
2018-02-25 00:13:39 +01:00
|
|
|
resolvedTypeArguments = this.program.resolveTypeArguments(
|
|
|
|
this.declaration.typeParameters,
|
|
|
|
typeArgumentNodes,
|
|
|
|
contextualTypeArguments,
|
|
|
|
alternativeReportNode
|
|
|
|
);
|
|
|
|
if (!resolvedTypeArguments) return null;
|
|
|
|
} else {
|
2017-12-15 15:00:19 +01:00
|
|
|
assert(typeArgumentNodes == null || !typeArgumentNodes.length);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-11-20 23:39:50 +01:00
|
|
|
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
|
|
|
}
|
2017-12-15 15:00:19 +01:00
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
toString(): string {
|
|
|
|
return this.simpleName;
|
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved class. */
|
2017-12-13 23:24:13 +01:00
|
|
|
export class Class extends Element {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.CLASS;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Prototype reference. */
|
|
|
|
prototype: ClassPrototype;
|
|
|
|
/** Resolved type arguments. */
|
2018-01-01 20:27:21 +01:00
|
|
|
typeArguments: Type[] | null;
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Resolved class type. */
|
2017-10-19 18:55:27 +02:00
|
|
|
type: Type;
|
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;
|
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(
|
|
|
|
prototype: ClassPrototype,
|
2018-03-31 18:18:55 +02:00
|
|
|
simpleName: string,
|
2018-02-25 00:13:39 +01:00
|
|
|
internalName: string,
|
|
|
|
typeArguments: Type[] | null = null,
|
|
|
|
base: Class | null = null
|
|
|
|
) {
|
2018-03-31 18:18:55 +02:00
|
|
|
super(prototype.program, simpleName, internalName);
|
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;
|
2018-01-21 17:52:44 +01:00
|
|
|
this.type = prototype.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) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
|
2018-04-11 23:35:19 +02:00
|
|
|
for (let [baseName, baseType] of inheritedTypeArguments) {
|
2018-01-05 18:19:32 +01:00
|
|
|
this.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
|
|
|
|
|
|
|
// apply instance-specific contextual type arguments
|
2017-12-28 04:09:40 +01:00
|
|
|
var declaration = this.prototype.declaration;
|
2018-01-01 20:27:21 +01:00
|
|
|
var i: i32, k: i32;
|
2017-12-05 13:35:14 +01:00
|
|
|
if (declaration) { // irrelevant for built-ins
|
2018-03-13 02:32:10 +01:00
|
|
|
let typeParameters = declaration.typeParameters;
|
2018-01-01 20:27:21 +01:00
|
|
|
if (typeArguments) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if ((k = typeArguments.length) != 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
|
|
|
}
|
2018-01-02 03:54:06 +01:00
|
|
|
if (k) {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
|
|
|
|
for (i = 0; i < k; ++i) {
|
2018-02-14 19:21:31 +01:00
|
|
|
this.contextualTypeArguments.set(typeParameters[i].name.text, typeArguments[i]);
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-01 20:27:21 +01:00
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
} else if (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
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2018-02-25 00:13:39 +01:00
|
|
|
toString(): string {
|
2018-03-31 18:18:55 +02:00
|
|
|
return this.simpleName;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 23:24:13 +01:00
|
|
|
/** A yet unresolved interface. */
|
2017-11-17 14:33:51 +01:00
|
|
|
export class InterfacePrototype extends ClassPrototype {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.INTERFACE_PROTOTYPE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Declaration reference. */
|
2018-02-09 02:31:48 +01:00
|
|
|
declaration: InterfaceDeclaration; // more specific
|
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(
|
|
|
|
program: Program,
|
|
|
|
simpleName: string,
|
|
|
|
internalName: string,
|
2018-04-11 23:35:19 +02:00
|
|
|
declaration: InterfaceDeclaration,
|
|
|
|
decoratorFlags: DecoratorFlags
|
2018-02-25 00:13:39 +01:00
|
|
|
) {
|
2018-04-11 23:35:19 +02:00
|
|
|
super(program, simpleName, internalName, declaration, decoratorFlags);
|
2017-10-19 18:55:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
/** A resolved interface. */
|
|
|
|
export class Interface extends Class {
|
2017-10-19 18:55:27 +02:00
|
|
|
|
2017-11-17 14:33:51 +01:00
|
|
|
kind = ElementKind.INTERFACE;
|
2017-12-13 04:46:05 +01:00
|
|
|
|
|
|
|
/** Prototype reference. */
|
2017-12-13 23:24:13 +01:00
|
|
|
prototype: InterfacePrototype; // more specific
|
2017-12-13 04:46:05 +01:00
|
|
|
/** Base interface, if applcable. */
|
2017-12-13 23:24:13 +01:00
|
|
|
base: Interface | null; // more specific
|
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(
|
|
|
|
prototype: InterfacePrototype,
|
2018-03-31 18:18:55 +02:00
|
|
|
simpleName: string,
|
2018-02-25 00:13:39 +01:00
|
|
|
internalName: string,
|
|
|
|
typeArguments: Type[] = [],
|
|
|
|
base: Interface | null = null
|
|
|
|
) {
|
2018-03-31 18:18:55 +02:00
|
|
|
super(prototype, simpleName, internalName, typeArguments, base);
|
2017-09-28 13:08:25 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
|
|
|
|
/** Control flow flags indicating specific conditions. */
|
|
|
|
export const enum FlowFlags {
|
2018-01-19 04:16:18 +01:00
|
|
|
/** No specific conditions. */
|
2018-01-18 23:34:12 +01:00
|
|
|
NONE = 0,
|
2018-03-23 01:47:01 +01:00
|
|
|
|
2018-01-19 04:16:18 +01:00
|
|
|
/** This branch always returns. */
|
2018-01-18 23:34:12 +01:00
|
|
|
RETURNS = 1 << 0,
|
2018-03-23 01:47:01 +01:00
|
|
|
/** This branch always throws. */
|
|
|
|
THROWS = 1 << 1,
|
|
|
|
/** This branch always breaks. */
|
|
|
|
BREAKS = 1 << 2,
|
|
|
|
/** This branch always continues. */
|
|
|
|
CONTINUES = 1 << 3,
|
|
|
|
/** This branch always allocates. Constructors only. */
|
|
|
|
ALLOCATES = 1 << 4,
|
|
|
|
|
|
|
|
/** This branch conditionally returns in a child branch. */
|
|
|
|
CONDITIONALLY_RETURNS = 1 << 5,
|
|
|
|
/** This branch conditionally throws in a child branch. */
|
|
|
|
CONDITIONALLY_THROWS = 1 << 6,
|
|
|
|
/** This branch conditionally breaks in a child branch. */
|
|
|
|
CONDITIONALLY_BREAKS = 1 << 7,
|
|
|
|
/** This branch conditionally continues in a child branch. */
|
|
|
|
CONDITIONALLY_CONTINUES = 1 << 8,
|
|
|
|
/** This branch conditionally allocates in a child branch. Constructors only. */
|
2018-04-11 23:35:19 +02:00
|
|
|
CONDITIONALLY_ALLOCATES = 1 << 9,
|
|
|
|
|
|
|
|
/** This branch is part of inlining a function. */
|
2018-04-25 05:04:35 +02:00
|
|
|
INLINE_CONTEXT = 1 << 10,
|
|
|
|
/** This branch explicitly requests no bounds checking. */
|
2018-05-06 00:00:54 +02:00
|
|
|
UNCHECKED_CONTEXT = 1 << 11,
|
|
|
|
/** This branch returns a properly wrapped value. */
|
2018-05-27 12:24:16 +02:00
|
|
|
RETURNS_WRAPPED = 1 << 12,
|
|
|
|
|
|
|
|
/** This branch is terminated if any of these flags is set. */
|
|
|
|
TERMINATED = FlowFlags.RETURNS | FlowFlags.THROWS | FlowFlags.BREAKS | FlowFlags.CONTINUES
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** A control flow evaluator. */
|
|
|
|
export class Flow {
|
|
|
|
|
|
|
|
/** Parent flow. */
|
|
|
|
parent: Flow | null;
|
|
|
|
/** Flow flags indicating specific conditions. */
|
|
|
|
flags: FlowFlags;
|
|
|
|
/** Function this flow belongs to. */
|
|
|
|
currentFunction: Function;
|
|
|
|
/** The label we break to when encountering a continue statement. */
|
|
|
|
continueLabel: string | null;
|
|
|
|
/** The label we break to when encountering a break statement. */
|
|
|
|
breakLabel: string | null;
|
2018-04-11 23:35:19 +02:00
|
|
|
/** The label we break to when encountering a return statement, when inlining. */
|
|
|
|
returnLabel: string | null;
|
|
|
|
/** The current return type. */
|
|
|
|
returnType: Type;
|
|
|
|
/** The current contextual type arguments. */
|
|
|
|
contextualTypeArguments: Map<string,Type> | null;
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Scoped local variables. */
|
|
|
|
scopedLocals: Map<string,Local> | null = null;
|
2018-05-06 00:00:54 +02:00
|
|
|
/** Local variable wrap states for the first 64 locals. */
|
|
|
|
wrappedLocals: I64;
|
|
|
|
/** Local variable wrap states for locals with index >= 64. */
|
|
|
|
wrappedLocalsExt: I64[] | null;
|
2018-01-18 23:34:12 +01:00
|
|
|
|
|
|
|
/** Creates the parent flow of the specified function. */
|
|
|
|
static create(currentFunction: Function): Flow {
|
|
|
|
var parentFlow = new Flow();
|
2018-01-19 04:16:18 +01:00
|
|
|
parentFlow.parent = null;
|
2018-01-18 23:34:12 +01:00
|
|
|
parentFlow.flags = FlowFlags.NONE;
|
|
|
|
parentFlow.currentFunction = currentFunction;
|
|
|
|
parentFlow.continueLabel = null;
|
|
|
|
parentFlow.breakLabel = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
parentFlow.returnLabel = null;
|
|
|
|
parentFlow.returnType = currentFunction.signature.returnType;
|
|
|
|
parentFlow.contextualTypeArguments = currentFunction.contextualTypeArguments;
|
2018-05-06 00:00:54 +02:00
|
|
|
parentFlow.wrappedLocals = i64_new(0);
|
|
|
|
parentFlow.wrappedLocalsExt = null;
|
2018-01-18 23:34:12 +01:00
|
|
|
return parentFlow;
|
|
|
|
}
|
|
|
|
|
|
|
|
private constructor() { }
|
|
|
|
|
|
|
|
/** Tests if this flow has the specified flag or flags. */
|
|
|
|
is(flag: FlowFlags): bool { return (this.flags & flag) == flag; }
|
2018-05-06 05:46:35 +02:00
|
|
|
/** Tests if this flow has one of the specified flags. */
|
2018-05-27 12:24:16 +02:00
|
|
|
isAny(flag: FlowFlags): bool { return (this.flags & flag) != 0; }
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Sets the specified flag or flags. */
|
|
|
|
set(flag: FlowFlags): void { this.flags |= flag; }
|
2018-03-23 01:47:01 +01:00
|
|
|
/** Unsets the specified flag or flags. */
|
|
|
|
unset(flag: FlowFlags): void { this.flags &= ~flag; }
|
2018-01-18 23:34:12 +01:00
|
|
|
|
2018-01-19 04:16:18 +01:00
|
|
|
/** Enters a new branch or scope and returns the new flow. */
|
|
|
|
enterBranchOrScope(): Flow {
|
2018-03-23 01:47:01 +01:00
|
|
|
var branch = new Flow();
|
|
|
|
branch.parent = this;
|
|
|
|
branch.flags = this.flags;
|
|
|
|
branch.currentFunction = this.currentFunction;
|
|
|
|
branch.continueLabel = this.continueLabel;
|
|
|
|
branch.breakLabel = this.breakLabel;
|
2018-04-11 23:35:19 +02:00
|
|
|
branch.returnLabel = this.returnLabel;
|
|
|
|
branch.returnType = this.returnType;
|
|
|
|
branch.contextualTypeArguments = this.contextualTypeArguments;
|
2018-05-06 00:00:54 +02:00
|
|
|
branch.wrappedLocals = this.wrappedLocals;
|
|
|
|
branch.wrappedLocalsExt = this.wrappedLocalsExt ? this.wrappedLocalsExt.slice() : null;
|
2018-03-23 01:47:01 +01:00
|
|
|
return branch;
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
|
|
|
|
2018-01-19 04:16:18 +01:00
|
|
|
/** Leaves the current branch or scope and returns the parent flow. */
|
2018-05-27 12:24:16 +02:00
|
|
|
leaveBranchOrScope(propagate: bool = true): Flow {
|
2018-01-19 04:16:18 +01:00
|
|
|
var parent = assert(this.parent);
|
2018-01-23 15:44:25 +01:00
|
|
|
|
|
|
|
// Free block-scoped locals
|
2018-01-18 23:34:12 +01:00
|
|
|
if (this.scopedLocals) {
|
2018-03-13 02:32:10 +01:00
|
|
|
for (let scopedLocal of this.scopedLocals.values()) {
|
2018-04-25 05:04:35 +02:00
|
|
|
if (scopedLocal.is(CommonFlags.SCOPED)) { // otherwise an alias
|
|
|
|
this.currentFunction.freeTempLocal(scopedLocal);
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
this.scopedLocals = null;
|
|
|
|
}
|
2018-01-23 15:44:25 +01:00
|
|
|
|
2018-05-06 00:00:54 +02:00
|
|
|
// Propagate conditionaal flags to parent
|
2018-05-27 12:24:16 +02:00
|
|
|
if (propagate) {
|
|
|
|
if (this.is(FlowFlags.RETURNS)) {
|
|
|
|
parent.set(FlowFlags.CONDITIONALLY_RETURNS);
|
|
|
|
}
|
|
|
|
if (this.is(FlowFlags.THROWS)) {
|
|
|
|
parent.set(FlowFlags.CONDITIONALLY_THROWS);
|
|
|
|
}
|
|
|
|
if (this.is(FlowFlags.BREAKS) && parent.breakLabel == this.breakLabel) {
|
|
|
|
parent.set(FlowFlags.CONDITIONALLY_BREAKS);
|
|
|
|
}
|
|
|
|
if (this.is(FlowFlags.CONTINUES) && parent.continueLabel == this.continueLabel) {
|
|
|
|
parent.set(FlowFlags.CONDITIONALLY_CONTINUES);
|
|
|
|
}
|
|
|
|
if (this.is(FlowFlags.ALLOCATES)) {
|
|
|
|
parent.set(FlowFlags.CONDITIONALLY_ALLOCATES);
|
|
|
|
}
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-19 04:16:18 +01:00
|
|
|
return parent;
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Adds a new scoped local of the specified name. */
|
2018-05-06 00:00:54 +02:00
|
|
|
addScopedLocal(type: Type, name: string, wrapped: bool, declaration?: VariableDeclaration): Local {
|
|
|
|
var scopedLocal = this.currentFunction.getTempLocal(type, false);
|
2018-02-25 00:13:39 +01:00
|
|
|
if (!this.scopedLocals) this.scopedLocals = new Map();
|
2018-04-11 23:35:19 +02:00
|
|
|
else {
|
|
|
|
let existingLocal = this.scopedLocals.get(name);
|
|
|
|
if (existingLocal) {
|
|
|
|
if (declaration) {
|
|
|
|
this.currentFunction.program.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range
|
|
|
|
);
|
|
|
|
} else assert(false);
|
|
|
|
return existingLocal;
|
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
2018-05-11 16:31:56 +02:00
|
|
|
scopedLocal.set(CommonFlags.SCOPED);
|
2018-01-18 23:34:12 +01:00
|
|
|
this.scopedLocals.set(name, scopedLocal);
|
2018-05-08 22:16:12 +02:00
|
|
|
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
|
|
|
|
this.setLocalWrapped(scopedLocal.index, wrapped);
|
|
|
|
}
|
2018-04-11 23:35:19 +02:00
|
|
|
return scopedLocal;
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
|
|
|
|
2018-04-25 05:04:35 +02:00
|
|
|
/** Adds a new scoped alias for the specified local. */
|
|
|
|
addScopedLocalAlias(index: i32, type: Type, name: string): Local {
|
|
|
|
if (!this.scopedLocals) this.scopedLocals = new Map();
|
|
|
|
else {
|
|
|
|
let existingLocal = this.scopedLocals.get(name);
|
|
|
|
if (existingLocal) {
|
|
|
|
let declaration = existingLocal.declaration;
|
|
|
|
if (declaration) {
|
|
|
|
this.currentFunction.program.error(
|
|
|
|
DiagnosticCode.Duplicate_identifier_0,
|
|
|
|
declaration.name.range
|
|
|
|
);
|
|
|
|
} else assert(false);
|
|
|
|
return existingLocal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(index < this.currentFunction.localsByIndex.length);
|
|
|
|
var scopedAlias = new Local( // not SCOPED as an indicator that it isn't automatically free'd
|
|
|
|
this.currentFunction.program,
|
|
|
|
name,
|
|
|
|
index,
|
|
|
|
type,
|
|
|
|
null
|
|
|
|
);
|
|
|
|
this.scopedLocals.set(name, scopedAlias);
|
|
|
|
return scopedAlias;
|
|
|
|
}
|
|
|
|
|
2018-01-18 23:34:12 +01:00
|
|
|
/** Gets the local of the specified name in the current scope. */
|
|
|
|
getScopedLocal(name: string): Local | null {
|
|
|
|
var local: Local | null;
|
|
|
|
var current: Flow | null = this;
|
|
|
|
do {
|
2018-02-25 00:13:39 +01:00
|
|
|
if (current.scopedLocals && (local = current.scopedLocals.get(name))) {
|
2018-01-18 23:34:12 +01:00
|
|
|
return local;
|
2018-02-25 00:13:39 +01:00
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
} while (current = current.parent);
|
2018-04-25 05:04:35 +02:00
|
|
|
return this.currentFunction.localsByName.get(name);
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
2018-01-19 04:16:18 +01:00
|
|
|
|
2018-05-06 00:00:54 +02:00
|
|
|
/** Tests if the local with the specified index is considered wrapped. */
|
|
|
|
isLocalWrapped(index: i32): bool {
|
|
|
|
var map: I64;
|
|
|
|
var ext: I64[] | null;
|
|
|
|
if (index < 64) {
|
|
|
|
if (index < 0) return true; // inlined constant
|
|
|
|
map = this.wrappedLocals;
|
|
|
|
} else if (ext = this.wrappedLocalsExt) {
|
|
|
|
let i = ((index - 64) / 64) | 0;
|
|
|
|
if (i >= ext.length) return false;
|
|
|
|
map = ext[i];
|
|
|
|
index -= (i + 1) * 64;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return i64_ne(
|
|
|
|
i64_and(
|
|
|
|
map,
|
|
|
|
i64_shl(
|
|
|
|
i64_one,
|
|
|
|
i64_new(index)
|
|
|
|
)
|
|
|
|
),
|
|
|
|
i64_zero
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sets if the local with the specified index is considered wrapped. */
|
|
|
|
setLocalWrapped(index: i32, wrapped: bool): void {
|
|
|
|
var map: I64;
|
2018-05-08 22:16:12 +02:00
|
|
|
var off: i32 = -1;
|
2018-05-06 00:00:54 +02:00
|
|
|
if (index < 64) {
|
|
|
|
if (index < 0) return; // inlined constant
|
|
|
|
map = this.wrappedLocals;
|
|
|
|
} else {
|
|
|
|
let ext = this.wrappedLocalsExt;
|
2018-05-08 22:16:12 +02:00
|
|
|
off = ((index - 64) / 64) | 0;
|
|
|
|
if (!ext) {
|
|
|
|
this.wrappedLocalsExt = ext = new Array(off + 1);
|
|
|
|
ext.length = 0;
|
|
|
|
}
|
|
|
|
while (ext.length <= off) ext.push(i64_new(0));
|
|
|
|
map = ext[off];
|
|
|
|
index -= (off + 1) * 64;
|
2018-05-06 00:00:54 +02:00
|
|
|
}
|
|
|
|
map = wrapped
|
|
|
|
? i64_or(
|
|
|
|
map,
|
|
|
|
i64_shl(
|
|
|
|
i64_one,
|
|
|
|
i64_new(index)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
: i64_and(
|
|
|
|
map,
|
|
|
|
i64_not(
|
|
|
|
i64_shl(
|
|
|
|
i64_one,
|
|
|
|
i64_new(index)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
2018-05-08 22:16:12 +02:00
|
|
|
if (off >= 0) (<I64[]>this.wrappedLocalsExt)[off] = map;
|
2018-05-06 00:00:54 +02:00
|
|
|
else this.wrappedLocals = map;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Inherits flags and local wrap states from the specified flow (e.g. on inner block). */
|
|
|
|
inherit(other: Flow): void {
|
|
|
|
this.flags |= other.flags & (
|
|
|
|
FlowFlags.RETURNS |
|
|
|
|
FlowFlags.RETURNS_WRAPPED |
|
|
|
|
FlowFlags.THROWS |
|
|
|
|
FlowFlags.BREAKS |
|
|
|
|
FlowFlags.CONTINUES |
|
|
|
|
FlowFlags.ALLOCATES
|
|
|
|
);
|
|
|
|
this.wrappedLocals = other.wrappedLocals;
|
|
|
|
this.wrappedLocalsExt = other.wrappedLocalsExt; // no need to slice because other flow is finished
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Inherits mutual flags and local wrap states from the specified flows (e.g. on then/else branches). */
|
|
|
|
inheritMutual(left: Flow, right: Flow): void {
|
|
|
|
// flags set in both arms
|
|
|
|
this.flags |= left.flags & right.flags & (
|
|
|
|
FlowFlags.RETURNS |
|
|
|
|
FlowFlags.RETURNS_WRAPPED |
|
|
|
|
FlowFlags.THROWS |
|
|
|
|
FlowFlags.BREAKS |
|
|
|
|
FlowFlags.CONTINUES |
|
|
|
|
FlowFlags.ALLOCATES
|
|
|
|
);
|
|
|
|
// locals wrapped in both arms
|
|
|
|
this.wrappedLocals = i64_and(
|
|
|
|
left.wrappedLocals,
|
|
|
|
right.wrappedLocals
|
|
|
|
);
|
|
|
|
var leftExt = left.wrappedLocalsExt;
|
|
|
|
var rightExt = right.wrappedLocalsExt;
|
|
|
|
if (leftExt != null && rightExt != null) {
|
|
|
|
let thisExt = this.wrappedLocalsExt;
|
|
|
|
let minLength = min(leftExt.length, rightExt.length);
|
|
|
|
if (minLength) {
|
|
|
|
if (!thisExt) thisExt = new Array(minLength);
|
|
|
|
else while (thisExt.length < minLength) thisExt.push(i64_new(0));
|
|
|
|
for (let i = 0; i < minLength; ++i) {
|
|
|
|
thisExt[i] = i64_and(
|
|
|
|
leftExt[i],
|
|
|
|
rightExt[i]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests if an expression can possibly overflow in the context of this flow. Assumes that the
|
|
|
|
* expression might already have overflown and returns `false` only if the operation neglects
|
|
|
|
* any possibly combination of garbage bits being present.
|
|
|
|
*/
|
|
|
|
canOverflow(expr: ExpressionRef, type: Type): bool {
|
|
|
|
// TODO: the following catches most common and a few uncommon cases, but there are additional
|
|
|
|
// opportunities here, obviously.
|
|
|
|
assert(type != Type.void);
|
|
|
|
|
|
|
|
// types other than i8, u8, i16, u16 and bool do not overflow
|
|
|
|
if (!type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) return false;
|
|
|
|
|
|
|
|
var operand: ExpressionRef;
|
|
|
|
switch (getExpressionId(expr)) {
|
|
|
|
|
|
|
|
// overflows if the local isn't wrapped or the conversion does
|
|
|
|
case ExpressionId.GetLocal: {
|
|
|
|
let currentFunction = this.currentFunction;
|
|
|
|
let local = currentFunction.localsByIndex[getGetLocalIndex(expr)];
|
|
|
|
return !currentFunction.flow.isLocalWrapped(local.index)
|
|
|
|
|| canConversionOverflow(local.type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the value does
|
|
|
|
case ExpressionId.SetLocal: {
|
|
|
|
assert(isTeeLocal(expr));
|
|
|
|
return this.canOverflow(getSetLocalValue(expr), type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// never overflows because globals are wrapped on set
|
|
|
|
case ExpressionId.GetGlobal: return false;
|
|
|
|
|
|
|
|
case ExpressionId.Binary: {
|
|
|
|
switch (getBinaryOp(expr)) {
|
|
|
|
|
|
|
|
// comparisons do not overflow (result is 0 or 1)
|
|
|
|
case BinaryOp.EqI32:
|
|
|
|
case BinaryOp.EqI64:
|
|
|
|
case BinaryOp.EqF32:
|
|
|
|
case BinaryOp.EqF64:
|
|
|
|
case BinaryOp.NeI32:
|
|
|
|
case BinaryOp.NeI64:
|
|
|
|
case BinaryOp.NeF32:
|
|
|
|
case BinaryOp.NeF64:
|
|
|
|
case BinaryOp.LtI32:
|
|
|
|
case BinaryOp.LtU32:
|
|
|
|
case BinaryOp.LtI64:
|
|
|
|
case BinaryOp.LtU64:
|
|
|
|
case BinaryOp.LtF32:
|
|
|
|
case BinaryOp.LtF64:
|
|
|
|
case BinaryOp.LeI32:
|
|
|
|
case BinaryOp.LeU32:
|
|
|
|
case BinaryOp.LeI64:
|
|
|
|
case BinaryOp.LeU64:
|
|
|
|
case BinaryOp.LeF32:
|
|
|
|
case BinaryOp.LeF64:
|
|
|
|
case BinaryOp.GtI32:
|
|
|
|
case BinaryOp.GtU32:
|
|
|
|
case BinaryOp.GtI64:
|
|
|
|
case BinaryOp.GtU64:
|
|
|
|
case BinaryOp.GtF32:
|
|
|
|
case BinaryOp.GtF64:
|
|
|
|
case BinaryOp.GeI32:
|
|
|
|
case BinaryOp.GeU32:
|
|
|
|
case BinaryOp.GeI64:
|
|
|
|
case BinaryOp.GeU64:
|
|
|
|
case BinaryOp.GeF32:
|
|
|
|
case BinaryOp.GeF64: return false;
|
|
|
|
|
|
|
|
// result won't overflow if one side is 0 or if one side is 1 and the other wrapped
|
|
|
|
case BinaryOp.MulI32: {
|
|
|
|
return !(
|
|
|
|
(
|
|
|
|
getExpressionId(operand = getBinaryLeft(expr)) == ExpressionId.Const &&
|
|
|
|
(
|
|
|
|
getConstValueI32(operand) == 0 ||
|
|
|
|
(
|
|
|
|
getConstValueI32(operand) == 1 &&
|
|
|
|
!this.canOverflow(getBinaryRight(expr), type)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) || (
|
|
|
|
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
|
|
|
|
(
|
|
|
|
getConstValueI32(operand) == 0 ||
|
|
|
|
(
|
|
|
|
getConstValueI32(operand) == 1 &&
|
|
|
|
!this.canOverflow(getBinaryLeft(expr), type)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// result won't overflow if one side is a constant less than this type's mask or one side
|
|
|
|
// is wrapped
|
|
|
|
case BinaryOp.AndI32: {
|
|
|
|
// note that computeSmallIntegerMask returns the mask minus the MSB for signed types
|
|
|
|
// because signed value garbage bits must be guaranteed to be equal to the MSB.
|
|
|
|
return !(
|
|
|
|
(
|
|
|
|
(
|
|
|
|
getExpressionId(operand = getBinaryLeft(expr)) == ExpressionId.Const &&
|
|
|
|
getConstValueI32(operand) <= type.computeSmallIntegerMask(Type.i32)
|
|
|
|
) || !this.canOverflow(operand, type)
|
|
|
|
) || (
|
|
|
|
(
|
|
|
|
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
|
|
|
|
getConstValueI32(operand) <= type.computeSmallIntegerMask(Type.i32)
|
|
|
|
) || !this.canOverflow(operand, type)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the shift doesn't clear potential garbage bits
|
|
|
|
case BinaryOp.ShlI32: {
|
|
|
|
let shift = 32 - type.size;
|
|
|
|
return getExpressionId(operand = getBinaryRight(expr)) != ExpressionId.Const
|
|
|
|
|| getConstValueI32(operand) < shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the value does and the shift doesn't clear potential garbage bits
|
|
|
|
case BinaryOp.ShrI32: {
|
|
|
|
let shift = 32 - type.size;
|
|
|
|
return this.canOverflow(getBinaryLeft(expr), type) && (
|
|
|
|
getExpressionId(operand = getBinaryRight(expr)) != ExpressionId.Const ||
|
|
|
|
getConstValueI32(operand) < shift
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the shift does not clear potential garbage bits. if an unsigned value is
|
|
|
|
// wrapped, it can't overflow.
|
|
|
|
case BinaryOp.ShrU32: {
|
|
|
|
let shift = 32 - type.size;
|
|
|
|
return type.is(TypeFlags.SIGNED)
|
|
|
|
? !(
|
|
|
|
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
|
|
|
|
getConstValueI32(operand) > shift // must clear MSB
|
|
|
|
)
|
|
|
|
: this.canOverflow(getBinaryLeft(expr), type) && !(
|
|
|
|
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
|
|
|
|
getConstValueI32(operand) >= shift // can leave MSB
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if any side does
|
|
|
|
case BinaryOp.DivU32:
|
|
|
|
case BinaryOp.RemI32:
|
|
|
|
case BinaryOp.RemU32: {
|
|
|
|
return this.canOverflow(getBinaryLeft(expr), type)
|
|
|
|
|| this.canOverflow(getBinaryRight(expr), type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ExpressionId.Unary: {
|
|
|
|
switch (getUnaryOp(expr)) {
|
|
|
|
|
|
|
|
// comparisons do not overflow (result is 0 or 1)
|
|
|
|
case UnaryOp.EqzI32:
|
|
|
|
case UnaryOp.EqzI64: return false;
|
|
|
|
|
|
|
|
// overflow if the maximum result (32) cannot be represented in the target type
|
|
|
|
case UnaryOp.ClzI32:
|
|
|
|
case UnaryOp.CtzI32:
|
|
|
|
case UnaryOp.PopcntI32: return type.size < 7;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the value cannot be represented in the target type
|
|
|
|
case ExpressionId.Const: {
|
|
|
|
let value: i32 = 0;
|
|
|
|
switch (getExpressionType(expr)) {
|
|
|
|
case NativeType.I32: { value = getConstValueI32(expr); break; }
|
|
|
|
case NativeType.I64: { value = getConstValueI64Low(expr); break; } // discards upper bits
|
|
|
|
case NativeType.F32: { value = i32(getConstValueF32(expr)); break; }
|
|
|
|
case NativeType.F64: { value = i32(getConstValueF64(expr)); break; }
|
|
|
|
default: assert(false);
|
|
|
|
}
|
|
|
|
switch (type.kind) {
|
|
|
|
case TypeKind.I8: return value < i8.MIN_VALUE || value > i8.MAX_VALUE;
|
|
|
|
case TypeKind.I16: return value < i16.MIN_VALUE || value > i16.MAX_VALUE;
|
|
|
|
case TypeKind.U8: return value < 0 || value > u8.MAX_VALUE;
|
|
|
|
case TypeKind.U16: return value < 0 || value > u16.MAX_VALUE;
|
|
|
|
case TypeKind.BOOL: return (value & ~1) != 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the conversion does
|
|
|
|
case ExpressionId.Load: {
|
|
|
|
let fromType: Type;
|
|
|
|
switch (getLoadBytes(expr)) {
|
|
|
|
case 1: { fromType = isLoadSigned(expr) ? Type.i8 : Type.u8; break; }
|
|
|
|
case 2: { fromType = isLoadSigned(expr) ? Type.i16 : Type.u16; break; }
|
|
|
|
default: { fromType = isLoadSigned(expr) ? Type.i32 : Type.u32; break; }
|
|
|
|
}
|
|
|
|
return canConversionOverflow(fromType, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the result does, which is either
|
|
|
|
// - the last expression of the block, by contract, if the block doesn't have a label
|
|
|
|
// - the last expression or the value of an inner br if the block has a label (TODO)
|
|
|
|
case ExpressionId.Block: {
|
|
|
|
if (!getBlockName(expr)) {
|
|
|
|
let size = assert(getBlockChildCount(expr));
|
|
|
|
let last = getBlockChild(expr, size - 1);
|
|
|
|
return this.canOverflow(last, type);
|
|
|
|
}
|
|
|
|
// actually, brs with a value that'd be handled here is not emitted atm
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if either side does
|
|
|
|
case ExpressionId.If: {
|
|
|
|
return this.canOverflow(getIfTrue(expr), type)
|
|
|
|
|| this.canOverflow(assert(getIfFalse(expr)), type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if either side does
|
|
|
|
case ExpressionId.Select: {
|
|
|
|
return this.canOverflow(getSelectThen(expr), type)
|
|
|
|
|| this.canOverflow(getSelectElse(expr), type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// overflows if the call does not return a wrapped value or the conversion does
|
|
|
|
case ExpressionId.Call: {
|
|
|
|
let program = this.currentFunction.program;
|
|
|
|
let instance = assert(program.instancesLookup.get(assert(getCallTarget(expr))));
|
|
|
|
assert(instance.kind == ElementKind.FUNCTION);
|
|
|
|
let returnType = (<Function>instance).signature.returnType;
|
|
|
|
return !(<Function>instance).flow.is(FlowFlags.RETURNS_WRAPPED)
|
|
|
|
|| canConversionOverflow(returnType, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// doesn't technically overflow
|
|
|
|
case ExpressionId.Unreachable: return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2018-04-03 23:56:48 +02:00
|
|
|
|
2018-01-19 04:16:18 +01:00
|
|
|
/** Finalizes this flow. Must be the topmost parent flow of the function. */
|
2018-03-23 01:47:01 +01:00
|
|
|
finalize(): void {
|
2018-04-11 23:35:19 +02:00
|
|
|
assert(this.parent == null); // must be the topmost parent flow
|
2018-01-19 04:16:18 +01:00
|
|
|
this.continueLabel = null;
|
|
|
|
this.breakLabel = null;
|
2018-04-11 23:35:19 +02:00
|
|
|
this.returnLabel = null;
|
|
|
|
this.contextualTypeArguments = null;
|
2018-01-19 04:16:18 +01:00
|
|
|
}
|
2018-01-18 23:34:12 +01:00
|
|
|
}
|
2018-05-06 00:00:54 +02:00
|
|
|
|
|
|
|
/** Tests if a conversion from one type to another can technically overflow. */
|
|
|
|
function canConversionOverflow(fromType: Type, toType: Type): bool {
|
|
|
|
var fromSize = fromType.byteSize;
|
|
|
|
var toSize = toType.byteSize;
|
|
|
|
return !fromType.is(TypeFlags.INTEGER) // non-i32 locals or returns
|
|
|
|
|| fromSize > toSize
|
|
|
|
|| fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED);
|
|
|
|
}
|