Implement reference counting (#592)

This commit is contained in:
Daniel Wirtz
2019-06-05 23:15:39 +02:00
committed by GitHub
parent 3ed76a97f0
commit 0484a6b740
601 changed files with 261645 additions and 146131 deletions

View File

@ -67,6 +67,7 @@ export enum NodeKind {
DO,
EMPTY,
EXPORT,
EXPORTDEFAULT,
EXPORTIMPORT,
EXPRESSION,
FOR,
@ -687,6 +688,9 @@ export abstract class Node {
range.source.normalizedPath
);
} else { // absolute
if (!normalizedPath.startsWith(LIBRARY_PREFIX)) {
normalizedPath = LIBRARY_PREFIX + normalizedPath;
}
stmt.normalizedPath = normalizedPath;
}
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
@ -698,6 +702,16 @@ export abstract class Node {
return stmt;
}
static createExportDefaultStatement(
declaration: DeclarationStatement,
range: Range
): ExportDefaultStatement {
var stmt = new ExportDefaultStatement();
stmt.declaration = declaration;
stmt.range = range;
return stmt;
}
static createExportImportStatement(
name: IdentifierExpression,
externalName: IdentifierExpression,
@ -782,10 +796,18 @@ export abstract class Node {
stmt.declarations = null;
stmt.namespaceName = identifier;
stmt.path = path;
stmt.normalizedPath = resolvePath(
normalizePath(path.value),
range.source.normalizedPath
);
var normalizedPath = normalizePath(path.value);
if (path.value.startsWith(".")) {
stmt.normalizedPath = resolvePath(
normalizedPath,
range.source.normalizedPath
);
} else {
if (!normalizedPath.startsWith(LIBRARY_PREFIX)) {
normalizedPath = LIBRARY_PREFIX + normalizedPath;
}
stmt.normalizedPath = normalizedPath;
}
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
return stmt;
}
@ -1162,7 +1184,7 @@ export enum DecoratorKind {
EXTERNAL,
BUILTIN,
LAZY,
START
UNSAFE
}
/** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
@ -1198,11 +1220,11 @@ export function decoratorNameToKind(name: Expression): DecoratorKind {
}
case CharCode.s: {
if (nameStr == "sealed") return DecoratorKind.SEALED;
if (nameStr == "start") return DecoratorKind.START;
break;
}
case CharCode.u: {
if (nameStr == "unmanaged") return DecoratorKind.UNMANAGED;
if (nameStr == "unsafe") return DecoratorKind.UNSAFE;
break;
}
}
@ -1762,6 +1784,14 @@ export class ExportStatement extends Statement {
isDeclare: bool;
}
/** Represents an `export default` statement. */
export class ExportDefaultStatement extends Statement {
kind = NodeKind.EXPORTDEFAULT;
/** Declaration being exported as default. */
declaration: DeclarationStatement;
}
/** Represents an expression that is used as a statement. */
export class ExpressionStatement extends Statement {
kind = NodeKind.EXPRESSION;

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,7 @@ export enum CommonFlags {
// Other
/** Is quoted. */
QUOTED = 1 << 28
}
@ -141,10 +142,6 @@ export namespace CommonSymbols {
export const this_ = "this";
export const super_ = "super";
export const constructor = "constructor";
}
/** Common standard library symbols. */
export namespace LibrarySymbols {
// constants
export const ASC_TARGET = "ASC_TARGET";
export const ASC_NO_TREESHAKING = "ASC_NO_TREESHAKING";
@ -174,17 +171,41 @@ export namespace LibrarySymbols {
export const V128 = "V128";
export const String = "String";
export const Array = "Array";
export const FixedArray = "FixedArray";
export const Set = "Set";
export const Map = "Map";
export const ArrayBufferView = "ArrayBufferView";
export const ArrayBuffer = "ArrayBuffer";
export const Math = "Math";
export const Mathf = "Mathf";
export const Int8Array = "Int8Array";
export const Int16Array = "Int16Array";
export const Int32Array = "Int32Array";
export const Int64Array = "Int64Array";
export const Uint8Array = "Uint8Array";
export const Uint8ClampedArray = "Uint8ClampedArray";
export const Uint16Array = "Uint16Array";
export const Uint32Array = "Uint32Array";
export const Uint64Array = "Uint64Array";
export const Float32Array = "Float32Array";
export const Float64Array = "Float64Array";
// runtime
export const memory = "memory";
export const allocate = "allocate";
export const abort = "abort";
export const main = "main";
// other
export const length = "length";
export const byteLength = "byteLength";
export const pow = "pow";
export const mod = "mod";
export const alloc = "__alloc";
export const realloc = "__realloc";
export const free = "__free";
export const retain = "__retain";
export const release = "__release";
export const collect = "__collect";
export const typeinfo = "__typeinfo";
export const instanceof_ = "__instanceof";
export const visit = "__visit";
export const allocArray = "__allocArray";
}
// shared
export { Feature } from "../std/assembly/shared/feature";
export { Target } from "../std/assembly/shared/target";
export { Typeinfo, TypeinfoFlags } from "../std/assembly/shared/typeinfo";

File diff suppressed because it is too large Load Diff

View File

@ -30,9 +30,9 @@ import {
getLoopBody,
getBreakName,
getBreakCondition,
getGetLocalIndex,
getSetLocalIndex,
getSetLocalValue,
getLocalGetIndex,
getLocalSetIndex,
getLocalSetValue,
getLoadOffset,
getLoadPtr,
getStoreOffset,
@ -173,20 +173,20 @@ export class Decompiler {
case ExpressionId.CallIndirect: {
throw new Error("not implemented");
}
case ExpressionId.GetLocal: {
case ExpressionId.LocalGet: {
this.push("$");
this.push(getGetLocalIndex(expr).toString(10));
this.push(getLocalGetIndex(expr).toString(10));
return;
}
case ExpressionId.SetLocal: {
case ExpressionId.LocalSet: {
this.push("$");
this.push(getSetLocalIndex(expr).toString(10));
this.push(getLocalSetIndex(expr).toString(10));
this.push(" = ");
this.decompileExpression(getSetLocalValue(expr));
this.decompileExpression(getLocalSetValue(expr));
return;
}
case ExpressionId.GetGlobal:
case ExpressionId.SetGlobal: {
case ExpressionId.GlobalGet:
case ExpressionId.GlobalSet: {
throw new Error("not implemented");
}
case ExpressionId.Load: {
@ -832,11 +832,11 @@ export class Decompiler {
}
case ExpressionId.Host: {
switch (getHostOp(expr)) {
case HostOp.CurrentMemory: {
case HostOp.MemorySize: {
this.push("memory.size()");
return;
}
case HostOp.GrowMemory: {
case HostOp.MemoryGrow: {
this.push("memory.grow(");
this.decompileExpression(getHostOperand(expr, 0));
this.push(")");

View File

@ -3,7 +3,7 @@
* @module definitions
*//***/
import {
import {
CommonFlags
} from "./common";
@ -23,7 +23,8 @@ import {
ConstantValueKind,
Interface,
Property,
PropertyPrototype
PropertyPrototype,
File
} from "./program";
import {
@ -42,10 +43,8 @@ abstract class ExportsWalker {
program: Program;
/** Whether to include private members */
includePrivate: bool;
/** Elements still to do. */
todo: Element[] = [];
/** Already seen elements. */
seen: Set<Element> = new Set();
seen: Map<Element,string> = new Map();
/** Constructs a new Element walker. */
constructor(program: Program, includePrivate: bool = false) {
@ -56,57 +55,66 @@ abstract class ExportsWalker {
/** Walks all elements and calls the respective handlers. */
walk(): void {
for (let file of this.program.filesByName.values()) {
let members = file.members;
if (!members) continue;
for (let member of members.values()) {
// FIXME: doesn't honor the actual externally visible name
this.visitElement(member);
}
if (file.source.isEntry) this.visitFile(file);
}
}
/** Visits all exported elements of a file. */
visitFile(file: File): void {
var members = file.exports;
if (members) {
for (let [name, member] of members) this.visitElement(name, member);
}
var exportsStar = file.exportsStar;
if (exportsStar) {
for (let exportStar of exportsStar) this.visitFile(exportStar);
}
var todo = this.todo;
for (let i = 0; i < todo.length; ) this.visitElement(todo[i]);
}
/** Visits an element.*/
visitElement(element: Element): void {
visitElement(name: string, element: Element): void {
if (element.is(CommonFlags.PRIVATE) && !this.includePrivate) return;
if (this.seen.has(element)) return;
this.seen.add(element);
var seen = this.seen;
if (seen.has(element)) {
this.visitAlias(name, element, <string>seen.get(element));
return;
}
seen.set(element, name);
switch (element.kind) {
case ElementKind.GLOBAL: {
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(<Global>element);
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(name, <Global>element);
break;
}
case ElementKind.ENUM: {
if (element.is(CommonFlags.COMPILED)) this.visitEnum(<Enum>element);
if (element.is(CommonFlags.COMPILED)) this.visitEnum(name, <Enum>element);
break;
}
case ElementKind.FUNCTION_PROTOTYPE: {
this.visitFunctionInstances(<FunctionPrototype>element);
this.visitFunctionInstances(name, <FunctionPrototype>element);
break;
}
case ElementKind.CLASS_PROTOTYPE: {
this.visitClassInstances(<ClassPrototype>element);
this.visitClassInstances(name, <ClassPrototype>element);
break;
}
case ElementKind.FIELD: {
if ((<Field>element).is(CommonFlags.COMPILED)) this.visitField(<Field>element);
if ((<Field>element).is(CommonFlags.COMPILED)) this.visitField(name, <Field>element);
break;
}
case ElementKind.PROPERTY_PROTOTYPE: {
this.visitPropertyInstances(<PropertyPrototype>element);
this.visitPropertyInstances(name, <PropertyPrototype>element);
break;
}
case ElementKind.PROPERTY: {
let prop = <Property>element;
let getter = prop.getterInstance;
if (getter) this.visitFunction(getter);
if (getter) this.visitFunction(name, getter);
let setter = prop.setterInstance;
if (setter) this.visitFunction(setter);
if (setter) this.visitFunction(name, setter);
break;
}
case ElementKind.NAMESPACE: {
if (hasCompiledMember(element)) this.visitNamespace(element);
if (hasCompiledMember(element)) this.visitNamespace(name, element);
break;
}
case ElementKind.TYPEDEFINITION: break;
@ -114,25 +122,25 @@ abstract class ExportsWalker {
}
}
private visitFunctionInstances(element: FunctionPrototype): void {
private visitFunctionInstances(name: string, element: FunctionPrototype): void {
var instances = element.instances;
if (instances) {
for (let instance of instances.values()) {
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(<Function>instance);
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(name, <Function>instance);
}
}
}
private visitClassInstances(element: ClassPrototype): void {
private visitClassInstances(name: string, element: ClassPrototype): void {
var instances = element.instances;
if (instances) {
for (let instance of instances.values()) {
if (instance.is(CommonFlags.COMPILED)) this.visitClass(<Class>instance);
if (instance.is(CommonFlags.COMPILED)) this.visitClass(name, <Class>instance);
}
}
}
private visitPropertyInstances(element: PropertyPrototype): void {
private visitPropertyInstances(name: string, element: PropertyPrototype): void {
// var instances = element.instances;
// if (instances) {
// for (let instance of instances.values()) {
@ -142,13 +150,14 @@ abstract class ExportsWalker {
assert(false);
}
abstract visitGlobal(element: Global): void;
abstract visitEnum(element: Enum): void;
abstract visitFunction(element: Function): void;
abstract visitClass(element: Class): void;
abstract visitInterface(element: Interface): void;
abstract visitField(element: Field): void;
abstract visitNamespace(element: Element): void;
abstract visitGlobal(name: string, element: Global): void;
abstract visitEnum(name: string, element: Enum): void;
abstract visitFunction(name: string, element: Function): void;
abstract visitClass(name: string, element: Class): void;
abstract visitInterface(name: string, element: Interface): void;
abstract visitField(name: string, element: Field): void;
abstract visitNamespace(name: string, element: Element): void;
abstract visitAlias(name: string, element: Element, originalName: string): void;
}
/** A WebIDL definitions builder. */
@ -167,14 +176,14 @@ export class IDLBuilder extends ExportsWalker {
super(program, includePrivate);
}
visitGlobal(element: Global): void {
visitGlobal(name: string, element: Global): void {
var sb = this.sb;
var isConst = element.is(CommonFlags.INLINED);
indent(sb, this.indentLevel);
if (isConst) sb.push("const ");
sb.push(this.typeToString(element.type));
sb.push(" ");
sb.push(element.name);
sb.push(name);
if (isConst) {
switch (element.constantValueKind) {
case ConstantValueKind.INTEGER: {
@ -193,11 +202,11 @@ export class IDLBuilder extends ExportsWalker {
sb.push(";\n");
}
visitEnum(element: Enum): void {
visitEnum(name: string, element: Enum): void {
var sb = this.sb;
indent(sb, this.indentLevel++);
sb.push("interface ");
sb.push(element.name);
sb.push(name);
sb.push(" {\n");
var members = element.members;
if (members) {
@ -218,20 +227,20 @@ export class IDLBuilder extends ExportsWalker {
}
}
for (let member of members.values()) {
if (member.kind != ElementKind.ENUMVALUE) this.visitElement(member);
if (member.kind != ElementKind.ENUMVALUE) this.visitElement(member.name, member);
}
}
indent(sb, --this.indentLevel);
sb.push("}\n");
}
visitFunction(element: Function): void {
visitFunction(name: string, element: Function): void {
var sb = this.sb;
var signature = element.signature;
indent(sb, this.indentLevel);
sb.push(this.typeToString(signature.returnType));
sb.push(" ");
sb.push(element.name);
sb.push(name);
sb.push("(");
var parameters = signature.parameterTypes;
var numParameters = parameters.length;
@ -250,45 +259,49 @@ export class IDLBuilder extends ExportsWalker {
sb.push("interface ");
sb.push(element.name);
sb.push(" {\n");
for (let member of members.values()) this.visitElement(member);
for (let member of members.values()) this.visitElement(member.name, member);
indent(sb, --this.indentLevel);
sb.push("}\n");
}
}
visitClass(element: Class): void {
visitClass(name: string, element: Class): void {
var sb = this.sb;
indent(sb, this.indentLevel++);
sb.push("interface ");
sb.push(element.name);
sb.push(name);
sb.push(" {\n");
// TODO
indent(sb, --this.indentLevel);
sb.push("}\n");
}
visitInterface(element: Interface): void {
this.visitClass(element);
visitInterface(name: string, element: Interface): void {
this.visitClass(name, element);
}
visitField(element: Field): void {
visitField(name: string, element: Field): void {
// TODO
}
visitNamespace(element: Namespace): void {
visitNamespace(name: string, element: Namespace): void {
var sb = this.sb;
indent(sb, this.indentLevel++);
sb.push("interface ");
sb.push(element.name);
sb.push(name);
sb.push(" {\n");
var members = element.members;
if (members) {
for (let member of members.values()) this.visitElement(member);
for (let member of members.values()) this.visitElement(member.name, member);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
}
visitAlias(name: string, element: Element, originalName: string): void {
// TODO
}
typeToString(type: Type): string {
switch (type.kind) {
case TypeKind.I8: return "byte";
@ -335,13 +348,14 @@ export class TSDBuilder extends ExportsWalker {
private sb: string[] = [];
private indentLevel: i32 = 0;
private unknown: Set<string> = new Set();
/** Constructs a new WebIDL builder. */
constructor(program: Program, includePrivate: bool = false) {
super(program, includePrivate);
}
visitGlobal(element: Global): void {
visitGlobal(name: string, element: Global): void {
var sb = this.sb;
var isConst = element.is(CommonFlags.INLINED);
indent(sb, this.indentLevel);
@ -349,21 +363,21 @@ export class TSDBuilder extends ExportsWalker {
if (isConst) sb.push("static readonly ");
else sb.push("static ");
} else {
if (isConst) sb.push("const ");
else sb.push("var ");
if (isConst) sb.push("export const ");
else sb.push("export var ");
}
sb.push(element.name);
sb.push(name);
sb.push(": ");
sb.push(this.typeToString(element.type));
sb.push(";\n");
this.visitNamespace(element);
this.visitNamespace(name, element);
}
visitEnum(element: Enum): void {
visitEnum(name: string, element: Enum): void {
var sb = this.sb;
indent(sb, this.indentLevel++);
sb.push("enum ");
sb.push(element.name);
sb.push("export enum ");
sb.push(name);
sb.push(" {\n");
var members = element.members;
if (members) {
@ -381,13 +395,13 @@ export class TSDBuilder extends ExportsWalker {
--numMembers;
}
}
if (numMembers) this.visitNamespace(element);
if (numMembers) this.visitNamespace(name, element);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
}
visitFunction(element: Function): void {
visitFunction(name: string, element: Function): void {
if (element.isAny(CommonFlags.PRIVATE | CommonFlags.SET)) return;
var sb = this.sb;
var signature = element.signature;
@ -395,14 +409,15 @@ export class TSDBuilder extends ExportsWalker {
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
if (element.is(CommonFlags.STATIC)) sb.push("static ");
if (element.is(CommonFlags.GET)) {
sb.push(element.identifierNode.text); // 'get:funcName' internally
sb.push(": ");
sb.push("get ");
sb.push(name); // 'get:funcName' internally
sb.push("(): ");
sb.push(this.typeToString(signature.returnType));
sb.push(";\n");
return;
} else {
if (!element.isAny(CommonFlags.STATIC | CommonFlags.INSTANCE)) sb.push("function ");
sb.push(element.name);
if (!element.isAny(CommonFlags.STATIC | CommonFlags.INSTANCE)) sb.push("export function ");
sb.push(name);
}
sb.push("(");
var parameters = signature.parameterTypes;
@ -422,73 +437,79 @@ export class TSDBuilder extends ExportsWalker {
sb.push(this.typeToString(signature.returnType));
}
sb.push(";\n");
this.visitNamespace(element);
this.visitNamespace(name, element);
}
visitClass(element: Class): void {
visitClass(name: string, element: Class): void {
var sb = this.sb;
var isInterface = element.kind == ElementKind.INTERFACE;
indent(sb, this.indentLevel++);
if (isInterface) {
sb.push("interface ");
sb.push("export interface ");
} else {
if (element.is(CommonFlags.ABSTRACT)) sb.push("abstract ");
sb.push("class ");
}
sb.push(element.name);
var base = element.base;
if (base && base.is(CommonFlags.COMPILED | CommonFlags.MODULE_EXPORT)) {
sb.push(" extends ");
sb.push(base.name); // TODO: fqn
sb.push("export class ");
}
sb.push(name);
// var base = element.base;
// if (base && base.is(CommonFlags.COMPILED | CommonFlags.MODULE_EXPORT)) {
// sb.push(" extends ");
// sb.push(base.name); // TODO: fqn
// }
sb.push(" {\n");
var members = element.parent.members; // static
if (members) {
for (let member of members.values()) {
this.visitElement(member);
}
var staticMembers = element.prototype.members;
if (staticMembers) {
for (let member of staticMembers.values()) this.visitElement(member.name, member);
}
var ctor = element.constructorInstance;
if (ctor) this.visitFunction(ctor);
members = element.members; // instance
if (members) {
for (let member of members.values()) this.visitElement(member);
var instanceMembers = element.members;
if (instanceMembers) {
for (let member of instanceMembers.values()) this.visitElement(member.name, member);
}
indent(sb, --this.indentLevel);
sb.push("}\n");
}
visitInterface(element: Interface): void {
this.visitClass(element);
visitInterface(name: string, element: Interface): void {
this.visitClass(name, element);
}
visitField(element: Field): void {
visitField(name: string, element: Field): void {
if (element.is(CommonFlags.PRIVATE)) return;
var sb = this.sb;
indent(sb, this.indentLevel);
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
if (element.is(CommonFlags.STATIC)) sb.push("static ");
if (element.is(CommonFlags.READONLY)) sb.push("readonly ");
sb.push(element.name);
sb.push(name);
sb.push(": ");
sb.push(this.typeToString(element.type));
sb.push(";\n");
}
visitNamespace(element: Element): void {
visitNamespace(name: string, element: Element): void {
var members = element.members;
if (members && members.size) {
let sb = this.sb;
indent(sb, this.indentLevel++);
sb.push("namespace ");
sb.push(element.name);
sb.push("export namespace ");
sb.push(name);
sb.push(" {\n");
for (let member of members.values()) this.visitElement(member);
for (let member of members.values()) this.visitElement(member.name, member);
indent(sb, --this.indentLevel);
sb.push("}\n");
}
}
visitAlias(name: string, element: Element, originalName: string): void {
var sb = this.sb;
indent(sb, this.indentLevel);
sb.push("export const ");
sb.push(name);
sb.push(" = typeof ");
sb.push(originalName);
sb.push(";\n");
}
typeToString(type: Type): string {
switch (type.kind) {
case TypeKind.I8: return "i8";
@ -510,7 +531,7 @@ export class TSDBuilder extends ExportsWalker {
case TypeKind.VOID: return "void";
default: {
assert(false);
return "";
return "any";
}
}
}

View File

@ -16,7 +16,7 @@ export enum DiagnosticCode {
Type_0_cannot_be_reinterpreted_as_type_1 = 203,
Basic_type_0_cannot_be_nullable = 204,
Cannot_export_a_mutable_global = 205,
Compiling_constant_with_non_constant_initializer_as_mutable = 206,
Mutable_value_cannot_be_inlined = 206,
Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa = 207,
Unmanaged_classes_cannot_implement_interfaces = 208,
Invalid_regular_expression_flags = 209,
@ -24,7 +24,7 @@ export enum DiagnosticCode {
Class_0_is_sealed_and_cannot_be_extended = 211,
Decorator_0_is_not_valid_here = 212,
Duplicate_decorator = 213,
An_allocator_must_be_declared_to_allocate_memory_Try_importing_allocator_arena_or_allocator_tlsf = 214,
An_allocator_must_be_present_to_use_0 = 214,
Optional_parameter_must_have_an_initializer = 215,
Constructor_of_class_0_must_not_require_any_arguments = 216,
Function_0_cannot_be_inlined_into_itself = 217,
@ -34,6 +34,8 @@ export enum DiagnosticCode {
Module_cannot_have_multiple_start_functions = 221,
_0_must_be_a_value_between_1_and_2_inclusive = 222,
_0_must_be_a_power_of_two = 223,
TODO_Cannot_inline_inferred_calls_and_specific_internals_yet = 224,
Expression_is_never_null = 225,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
@ -64,6 +66,7 @@ export enum DiagnosticCode {
Type_expected = 1110,
A_default_clause_cannot_appear_more_than_once_in_a_switch_statement = 1113,
Duplicate_label_0 = 1114,
An_export_assignment_cannot_have_modifiers = 1120,
Octal_literals_are_not_allowed_in_strict_mode = 1121,
Digit_expected = 1124,
Hexadecimal_digit_expected = 1125,
@ -120,6 +123,7 @@ export enum DiagnosticCode {
The_0_operator_cannot_be_applied_to_type_1 = 2469,
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
Object_is_possibly_null = 2531,
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
Index_signature_in_type_0_only_permits_reading = 2542,
@ -151,7 +155,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
case 204: return "Basic type '{0}' cannot be nullable.";
case 205: return "Cannot export a mutable global.";
case 206: return "Compiling constant with non-constant initializer as mutable.";
case 206: return "Mutable value cannot be inlined.";
case 207: return "Unmanaged classes cannot extend managed classes and vice-versa.";
case 208: return "Unmanaged classes cannot implement interfaces.";
case 209: return "Invalid regular expression flags.";
@ -159,7 +163,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 211: return "Class '{0}' is sealed and cannot be extended.";
case 212: return "Decorator '{0}' is not valid here.";
case 213: return "Duplicate decorator.";
case 214: return "An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.";
case 214: return "An allocator must be present to use '{0}'.";
case 215: return "Optional parameter must have an initializer.";
case 216: return "Constructor of class '{0}' must not require any arguments.";
case 217: return "Function '{0}' cannot be inlined into itself.";
@ -169,6 +173,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 221: return "Module cannot have multiple start functions.";
case 222: return "'{0}' must be a value between '{1}' and '{2}' inclusive.";
case 223: return "'{0}' must be a power of two.";
case 224: return "TODO: Cannot inline inferred calls and specific internals yet.";
case 225: return "Expression is never 'null'.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";
@ -199,6 +205,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1110: return "Type expected.";
case 1113: return "A 'default' clause cannot appear more than once in a 'switch' statement.";
case 1114: return "Duplicate label '{0}'.";
case 1120: return "An export assignment cannot have modifiers.";
case 1121: return "Octal literals are not allowed in strict mode.";
case 1124: return "Digit expected.";
case 1125: return "Hexadecimal digit expected.";
@ -255,6 +262,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
case 2531: return "Object is possibly 'null'.";
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
case 2541: return "The target of an assignment must be a variable or a property access.";
case 2542: return "Index signature in type '{0}' only permits reading.";

View File

@ -8,7 +8,7 @@
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
"Basic type '{0}' cannot be nullable.": 204,
"Cannot export a mutable global.": 205,
"Compiling constant with non-constant initializer as mutable.": 206,
"Mutable value cannot be inlined.": 206,
"Unmanaged classes cannot extend managed classes and vice-versa.": 207,
"Unmanaged classes cannot implement interfaces.": 208,
"Invalid regular expression flags.": 209,
@ -16,7 +16,7 @@
"Class '{0}' is sealed and cannot be extended.": 211,
"Decorator '{0}' is not valid here.": 212,
"Duplicate decorator.": 213,
"An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.": 214,
"An allocator must be present to use '{0}'.": 214,
"Optional parameter must have an initializer.": 215,
"Constructor of class '{0}' must not require any arguments.": 216,
"Function '{0}' cannot be inlined into itself.": 217,
@ -26,6 +26,8 @@
"Module cannot have multiple start functions.": 221,
"'{0}' must be a value between '{1}' and '{2}' inclusive.": 222,
"'{0}' must be a power of two.": 223,
"TODO: Cannot inline inferred calls and specific internals yet.": 224,
"Expression is never 'null'.": 225,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,
@ -57,6 +59,7 @@
"Type expected.": 1110,
"A 'default' clause cannot appear more than once in a 'switch' statement.": 1113,
"Duplicate label '{0}'.": 1114,
"An export assignment cannot have modifiers.": 1120,
"Octal literals are not allowed in strict mode.": 1121,
"Digit expected.": 1124,
"Hexadecimal digit expected.": 1125,
@ -114,6 +117,7 @@
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
"In 'const' enum declarations member initializer must be constant expression.": 2474,
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
"Object is possibly 'null'.": 2531,
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
"The target of an assignment must be a variable or a property access.": 2541,
"Index signature in type '{0}' only permits reading.": 2542,

View File

@ -14,7 +14,7 @@ import {
} from "./diagnosticMessages.generated";
import {
isLineBreak
isLineBreak, CharCode
} from "./util";
export {
@ -244,7 +244,13 @@ export function formatDiagnosticContext(range: Range, useColors: bool = false):
if (range.start == range.end) {
sb.push("^");
} else {
while (start++ < range.end) sb.push("~");
while (start++ < range.end) {
if (isLineBreak(text.charCodeAt(start))) {
sb.push(start == range.start + 1 ? "^" : "~");
break;
}
sb.push("~");
}
}
if (useColors) sb.push(COLOR_RESET);
return sb.join("");

View File

@ -52,6 +52,7 @@ import {
EmptyStatement,
ExportImportStatement,
ExportStatement,
ExportDefaultStatement,
ExpressionStatement,
ForStatement,
IfStatement,
@ -230,6 +231,10 @@ export class ASTBuilder {
this.visitExportStatement(<ExportStatement>node);
break;
}
case NodeKind.EXPORTDEFAULT: {
this.visitExportDefaultStatement(<ExportDefaultStatement>node);
break;
}
case NodeKind.EXPORTIMPORT: {
this.visitExportImportStatement(<ExportImportStatement>node);
break;
@ -851,15 +856,19 @@ export class ASTBuilder {
}
}
visitClassDeclaration(node: ClassDeclaration): void {
visitClassDeclaration(node: ClassDeclaration, isDefault: bool = false): void {
var decorators = node.decorators;
if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) {
this.serializeDecorator(decorators[i]);
}
}
this.serializeExternalModifiers(node);
var sb = this.sb;
if (isDefault) {
sb.push("export default ");
} else {
this.serializeExternalModifiers(node);
}
if (node.is(CommonFlags.ABSTRACT)) sb.push("abstract ");
if (node.name.text.length) {
sb.push("class ");
@ -931,9 +940,13 @@ export class ASTBuilder {
visitEmptyStatement(node: EmptyStatement): void {
}
visitEnumDeclaration(node: EnumDeclaration): void {
visitEnumDeclaration(node: EnumDeclaration, isDefault: bool = false): void {
var sb = this.sb;
this.serializeExternalModifiers(node);
if (isDefault) {
sb.push("export default ");
} else {
this.serializeExternalModifiers(node);
}
if (node.is(CommonFlags.CONST)) sb.push("const ");
sb.push("enum ");
this.visitIdentifierExpression(node.name);
@ -1011,6 +1024,33 @@ export class ASTBuilder {
sb.push(";");
}
visitExportDefaultStatement(node: ExportDefaultStatement): void {
var declaration = node.declaration;
switch (declaration.kind) {
case NodeKind.ENUMDECLARATION: {
this.visitEnumDeclaration(<EnumDeclaration>declaration, true);
break;
}
case NodeKind.FUNCTIONDECLARATION: {
this.visitFunctionDeclaration(<FunctionDeclaration>declaration, true);
break;
}
case NodeKind.CLASSDECLARATION: {
this.visitClassDeclaration(<ClassDeclaration>declaration, true);
break;
}
case NodeKind.INTERFACEDECLARATION: {
this.visitInterfaceDeclaration(<InterfaceDeclaration>declaration, true);
break;
}
case NodeKind.NAMESPACEDECLARATION: {
this.visitNamespaceDeclaration(<NamespaceDeclaration>declaration, true);
break;
}
default: assert(false);
}
}
visitExpressionStatement(node: ExpressionStatement): void {
this.visitNode(node.expression);
}
@ -1065,7 +1105,7 @@ export class ASTBuilder {
this.visitNode(node.statement);
}
visitFunctionDeclaration(node: FunctionDeclaration): void {
visitFunctionDeclaration(node: FunctionDeclaration, isDefault: bool = false): void {
var sb = this.sb;
var decorators = node.decorators;
if (decorators) {
@ -1073,8 +1113,12 @@ export class ASTBuilder {
this.serializeDecorator(decorators[i]);
}
}
this.serializeExternalModifiers(node);
this.serializeAccessModifiers(node);
if (isDefault) {
sb.push("export default ");
} else {
this.serializeExternalModifiers(node);
this.serializeAccessModifiers(node);
}
if (node.name.text.length) {
sb.push("function ");
} else {
@ -1230,15 +1274,19 @@ export class ASTBuilder {
this.visitTypeNode(node.valueType);
}
visitInterfaceDeclaration(node: InterfaceDeclaration): void {
visitInterfaceDeclaration(node: InterfaceDeclaration, isDefault: bool = false): void {
var decorators = node.decorators;
if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) {
this.serializeDecorator(decorators[i]);
}
}
this.serializeExternalModifiers(node);
var sb = this.sb;
if (isDefault) {
sb.push("export default ");
} else {
this.serializeExternalModifiers(node);
}
sb.push("interface ");
this.visitIdentifierExpression(node.name);
var typeParameters = node.typeParameters;
@ -1284,15 +1332,19 @@ export class ASTBuilder {
this.visitFunctionCommon(node);
}
visitNamespaceDeclaration(node: NamespaceDeclaration): void {
visitNamespaceDeclaration(node: NamespaceDeclaration, isDefault: bool = false): void {
var decorators = node.decorators;
if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) {
this.serializeDecorator(decorators[i]);
}
}
this.serializeExternalModifiers(node);
var sb = this.sb;
if (isDefault) {
sb.push("export default ");
} else {
this.serializeExternalModifiers(node);
}
sb.push("namespace ");
this.visitIdentifierExpression(node.name);
var members = node.members;

View File

@ -23,10 +23,10 @@ import {
ExpressionRef,
getExpressionId,
getGetLocalIndex,
isTeeLocal,
getSetLocalValue,
getGetGlobalName,
getLocalGetIndex,
isLocalTee,
getLocalSetValue,
getGlobalGetName,
getBinaryOp,
BinaryOp,
getBinaryLeft,
@ -47,7 +47,13 @@ import {
getIfFalse,
getSelectThen,
getSelectElse,
getCallTarget
getCallTarget,
getLocalSetIndex,
getIfCondition,
getConstValueI64High,
getUnaryValue,
getCallOperand,
traverse
} from "./module";
import {
@ -62,11 +68,6 @@ import {
Node
} from "./ast";
import {
bitsetIs,
bitsetSet
} from "./util";
/** Control flow flags indicating specific conditions. */
export const enum FlowFlags {
/** No specific conditions. */
@ -78,36 +79,38 @@ export const enum FlowFlags {
RETURNS = 1 << 0,
/** This flow returns a wrapped value. */
RETURNS_WRAPPED = 1 << 1,
/** This flow returns a non-null value. */
RETURNS_NONNULL = 1 << 2,
/** This flow throws. */
THROWS = 1 << 2,
THROWS = 1 << 3,
/** This flow breaks. */
BREAKS = 1 << 3,
BREAKS = 1 << 4,
/** This flow continues. */
CONTINUES = 1 << 4,
CONTINUES = 1 << 5,
/** This flow allocates. Constructors only. */
ALLOCATES = 1 << 5,
ALLOCATES = 1 << 6,
/** This flow calls super. Constructors only. */
CALLS_SUPER = 1 << 6,
CALLS_SUPER = 1 << 7,
// conditional
/** This flow conditionally returns in a child flow. */
CONDITIONALLY_RETURNS = 1 << 7,
CONDITIONALLY_RETURNS = 1 << 8,
/** This flow conditionally throws in a child flow. */
CONDITIONALLY_THROWS = 1 << 8,
CONDITIONALLY_THROWS = 1 << 9,
/** This flow conditionally breaks in a child flow. */
CONDITIONALLY_BREAKS = 1 << 9,
CONDITIONALLY_BREAKS = 1 << 10,
/** This flow conditionally continues in a child flow. */
CONDITIONALLY_CONTINUES = 1 << 10,
CONDITIONALLY_CONTINUES = 1 << 11,
/** This flow conditionally allocates in a child flow. Constructors only. */
CONDITIONALLY_ALLOCATES = 1 << 11,
CONDITIONALLY_ALLOCATES = 1 << 12,
// special
/** This is an inlining flow. */
INLINE_CONTEXT = 1 << 12,
INLINE_CONTEXT = 1 << 13,
/** This is a flow with explicitly disabled bounds checking. */
UNCHECKED_CONTEXT = 1 << 13,
UNCHECKED_CONTEXT = 1 << 14,
// masks
@ -120,6 +123,7 @@ export const enum FlowFlags {
/** Any categorical flag. */
ANY_CATEGORICAL = FlowFlags.RETURNS
| FlowFlags.RETURNS_WRAPPED
| FlowFlags.RETURNS_NONNULL
| FlowFlags.THROWS
| FlowFlags.BREAKS
| FlowFlags.CONTINUES
@ -134,6 +138,79 @@ export const enum FlowFlags {
| FlowFlags.CONDITIONALLY_ALLOCATES
}
/** Flags indicating the current state of a local. */
export enum LocalFlags {
/** No specific conditions. */
NONE = 0,
/** Local is constant. */
CONSTANT = 1 << 0,
/** Local is properly wrapped. Relevant for small integers. */
WRAPPED = 1 << 1,
/** Local is non-null. */
NONNULL = 1 << 2,
/** Local is read from. */
READFROM = 1 << 3,
/** Local is written to. */
WRITTENTO = 1 << 4,
/** Local is retained. */
RETAINED = 1 << 5,
/** Local is conditionally read from. */
CONDITIONALLY_READFROM = 1 << 6,
/** Local is conditionally written to. */
CONDITIONALLY_WRITTENTO = 1 << 7,
/** Local must be conditionally retained. */
CONDITIONALLY_RETAINED = 1 << 8,
/** Any categorical flag. */
ANY_CATEGORICAL = CONSTANT
| WRAPPED
| NONNULL
| READFROM
| WRITTENTO
| RETAINED,
/** Any conditional flag. */
ANY_CONDITIONAL = RETAINED
| CONDITIONALLY_READFROM
| CONDITIONALLY_WRITTENTO
| CONDITIONALLY_RETAINED,
/** Any retained flag. */
ANY_RETAINED = RETAINED
| CONDITIONALLY_RETAINED
}
export namespace LocalFlags {
export function join(left: LocalFlags, right: LocalFlags): LocalFlags {
return ((left & LocalFlags.ANY_CATEGORICAL) & (right & LocalFlags.ANY_CATEGORICAL))
| (left & LocalFlags.ANY_CONDITIONAL) | (right & LocalFlags.ANY_CONDITIONAL);
}
}
/** Flags indicating the current state of a field. */
export enum FieldFlags {
/** No specific conditions. */
NONE = 0,
/** Field is initialized. Relevant in constructors. */
INITIALIZED = 1 << 0,
/** Field is conditionally initialized. Relevant in constructors. */
CONDITIONALLY_INITIALIZED = 1 << 1,
/** Any categorical flag. */
ANY_CATEGORICAL = INITIALIZED,
/** Any conditional flag. */
ANY_CONDITIONAL = CONDITIONALLY_INITIALIZED
}
export namespace FieldFlags {
export function join(left: FieldFlags, right: FieldFlags): FieldFlags {
return ((left & FieldFlags.ANY_CATEGORICAL) & (right & FieldFlags.ANY_CATEGORICAL))
| (left & FieldFlags.ANY_CONDITIONAL) | (right & FieldFlags.ANY_CONDITIONAL);
}
}
/** A control flow evaluator. */
export class Flow {
@ -153,10 +230,10 @@ export class Flow {
contextualTypeArguments: Map<string,Type> | null;
/** Scoped local variables. */
scopedLocals: Map<string,Local> | null = null;
/** Local variable wrap states for the first 64 locals. */
wrappedLocals: I64;
/** Local variable wrap states for locals with index >= 64. */
wrappedLocalsExt: I64[] | null;
/** Local flags. */
localFlags: LocalFlags[];
/** Field flags. Relevant in constructors. */
fieldFlags: Map<string,FieldFlags> | null = null;
/** Function being inlined, when inlining. */
inlineFunction: Function | null;
/** The label we break to when encountering a return statement, when inlining. */
@ -172,14 +249,13 @@ export class Flow {
flow.breakLabel = null;
flow.returnType = parentFunction.signature.returnType;
flow.contextualTypeArguments = parentFunction.contextualTypeArguments;
flow.wrappedLocals = i64_new(0);
flow.wrappedLocalsExt = null;
flow.localFlags = [];
flow.inlineFunction = null;
flow.inlineReturnLabel = null;
return flow;
}
/** Creates an inline flow within `currentFunction`. */
/** Creates an inline flow within `parentFunction`. */
static createInline(parentFunction: Function, inlineFunction: Function): Flow {
var flow = Flow.create(parentFunction);
flow.set(FlowFlags.INLINE_CONTEXT);
@ -216,15 +292,14 @@ export class Flow {
branch.breakLabel = this.breakLabel;
branch.returnType = this.returnType;
branch.contextualTypeArguments = this.contextualTypeArguments;
branch.wrappedLocals = this.wrappedLocals;
branch.wrappedLocalsExt = this.wrappedLocalsExt ? this.wrappedLocalsExt.slice() : null;
branch.localFlags = this.localFlags.slice();
branch.inlineFunction = this.inlineFunction;
branch.inlineReturnLabel = this.inlineReturnLabel;
return branch;
}
/** Gets a free temporary local of the specified type. */
getTempLocal(type: Type, wrapped: bool = false): Local {
getTempLocal(type: Type, except: Set<i32> | null = null): Local {
var parentFunction = this.parentFunction;
var temps: Local[] | null;
switch (type.toNativeType()) {
@ -236,14 +311,43 @@ export class Flow {
default: throw new Error("concrete type expected");
}
var local: Local;
if (temps && temps.length) {
local = temps.pop();
local.type = type;
local.flags = CommonFlags.NONE;
} else {
if (except) {
if (temps && temps.length) {
for (let i = 0, k = temps.length; i < k; ++i) {
if (!except.has(temps[i].index)) {
local = temps[i];
let k = temps.length - 1;
while (i < k) unchecked(temps[i] = temps[i++ + 1]);
temps.length = k;
local.type = type;
local.flags = CommonFlags.NONE;
this.unsetLocalFlag(local.index, ~0);
return local;
}
}
}
local = parentFunction.addLocal(type);
} else {
if (temps && temps.length) {
local = temps.pop();
local.type = type;
local.flags = CommonFlags.NONE;
} else {
local = parentFunction.addLocal(type);
}
}
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) this.setLocalWrapped(local.index, wrapped);
this.unsetLocalFlag(local.index, ~0);
return local;
}
/** Gets a local that sticks around until this flow is exited, and then released. */
getAutoreleaseLocal(type: Type, except: Set<i32> | null = null): Local {
var local = this.getTempLocal(type, except);
local.set(CommonFlags.SCOPED);
var scopedLocals = this.scopedLocals;
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
scopedLocals.set("~auto" + (this.parentFunction.nextAutoreleaseId++), local);
this.setLocalFlag(local.index, LocalFlags.RETAINED);
return local;
}
@ -282,65 +386,27 @@ export class Flow {
}
/** Gets and immediately frees a temporary local of the specified type. */
getAndFreeTempLocal(type: Type, wrapped: bool): Local {
var parentFunction = this.parentFunction;
var temps: Local[];
switch (type.toNativeType()) {
case NativeType.I32: {
temps = parentFunction.tempI32s || (parentFunction.tempI32s = []);
break;
}
case NativeType.I64: {
temps = parentFunction.tempI64s || (parentFunction.tempI64s = []);
break;
}
case NativeType.F32: {
temps = parentFunction.tempF32s || (parentFunction.tempF32s = []);
break;
}
case NativeType.F64: {
temps = parentFunction.tempF64s || (parentFunction.tempF64s = []);
break;
}
case NativeType.V128: {
temps = parentFunction.tempV128s || (parentFunction.tempV128s = []);
break;
}
default: throw new Error("concrete type expected");
}
var local: Local;
if (temps.length) {
local = temps[temps.length - 1];
local.type = type;
} else {
local = parentFunction.addLocal(type);
temps.push(local);
}
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) this.setLocalWrapped(local.index, wrapped);
getAndFreeTempLocal(type: Type, except: Set<i32> | null = null): Local {
var local = this.getTempLocal(type, except);
this.freeTempLocal(local);
return local;
}
/** Gets the scoped local of the specified name. */
getScopedLocal(name: string): Local | null {
var scopedLocals = this.scopedLocals;
if (scopedLocals && scopedLocals.has(name)) return scopedLocals.get(name);
return null;
}
/** Adds a new scoped local of the specified name. */
addScopedLocal(name: string, type: Type, wrapped: bool, reportNode: Node | null = null): Local {
var scopedLocal = this.getTempLocal(type, false);
if (!this.scopedLocals) this.scopedLocals = new Map();
else {
let existingLocal = this.scopedLocals.get(name);
if (existingLocal) {
if (reportNode) {
this.parentFunction.program.error(
DiagnosticCode.Duplicate_identifier_0,
reportNode.range
);
}
return existingLocal;
}
}
addScopedLocal(name: string, type: Type, except: Set<i32> | null = null): Local {
var scopedLocal = this.getTempLocal(type, except);
var scopedLocals = this.scopedLocals;
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
else assert(!scopedLocals.has(name));
scopedLocal.set(CommonFlags.SCOPED);
this.scopedLocals.set(name, scopedLocal);
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
this.setLocalWrapped(scopedLocal.index, wrapped);
}
scopedLocals.set(name, scopedLocal);
return scopedLocal;
}
@ -360,12 +426,7 @@ export class Flow {
}
}
assert(index < this.parentFunction.localsByIndex.length);
var scopedAlias = new Local(
name,
index,
type,
this.parentFunction
);
var scopedAlias = new Local(name, index, type, this.parentFunction);
// not flagged as SCOPED as it must not be free'd when the flow is finalized
this.scopedLocals.set(name, scopedAlias);
return scopedAlias;
@ -399,32 +460,34 @@ export class Flow {
return this.actualFunction.lookup(name);
}
/** Tests if the value of the local at the specified index is considered wrapped. */
isLocalWrapped(index: i32): bool {
if (index < 0) return true; // inlined constant
if (index < 64) return bitsetIs(this.wrappedLocals, index);
var ext = this.wrappedLocalsExt;
var i = ((index - 64) / 64) | 0;
if (!(ext && i < ext.length)) return false;
return bitsetIs(ext[i], index - (i + 1) * 64);
/** Tests if the local at the specified index has the specified flag or flags. */
isLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
if (index < 0) return defaultIfInlined;
var localFlags = this.localFlags;
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) == flag;
}
/** Sets if the value of the local at the specified index is considered wrapped. */
setLocalWrapped(index: i32, wrapped: bool): void {
if (index < 0) return; // inlined constant
if (index < 64) {
this.wrappedLocals = bitsetSet(this.wrappedLocals, index, wrapped);
return;
}
var ext = this.wrappedLocalsExt;
var i = ((index - 64) / 64) | 0;
if (!ext) {
this.wrappedLocalsExt = ext = new Array(i + 1);
for (let j = 0; j <= i; ++j) ext[j] = i64_new(0);
} else {
while (ext.length <= i) ext.push(i64_new(0));
}
ext[i] = bitsetSet(ext[i], index - (i + 1) * 64, wrapped);
/** Tests if the local at the specified index has any of the specified flags. */
isAnyLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
if (index < 0) return defaultIfInlined;
var localFlags = this.localFlags;
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) != 0;
}
/** Sets the specified flag or flags on the local at the specified index. */
setLocalFlag(index: i32, flag: LocalFlags): void {
if (index < 0) return;
var localFlags = this.localFlags;
var flags = index < localFlags.length ? unchecked(localFlags[index]) : 0;
this.localFlags[index] = flags | flag;
}
/** Unsets the specified flag or flags on the local at the specified index. */
unsetLocalFlag(index: i32, flag: LocalFlags): void {
if (index < 0) return;
var localFlags = this.localFlags;
var flags = index < localFlags.length ? unchecked(localFlags[index]) : 0;
this.localFlags[index] = flags & ~flag;
}
/** Pushes a new break label to the stack, for example when entering a loop that one can `break` from. */
@ -454,8 +517,7 @@ export class Flow {
/** Inherits flags and local wrap states from the specified flow (e.g. blocks). */
inherit(other: Flow): void {
this.flags |= other.flags & (FlowFlags.ANY_CATEGORICAL | FlowFlags.ANY_CONDITIONAL);
this.wrappedLocals = other.wrappedLocals;
this.wrappedLocalsExt = other.wrappedLocalsExt; // no need to slice because other flow is finished
this.localFlags = other.localFlags; // no need to slice because other flow is finished
}
/** Inherits categorical flags as conditional flags from the specified flow (e.g. then without else). */
@ -475,33 +537,245 @@ export class Flow {
if (other.is(FlowFlags.ALLOCATES)) {
this.set(FlowFlags.CONDITIONALLY_ALLOCATES);
}
var localFlags = other.localFlags;
for (let i = 0, k = localFlags.length; i < k; ++i) {
let flags = localFlags[i];
if (flags & LocalFlags.RETAINED) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_RETAINED);
if (flags & LocalFlags.READFROM) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_READFROM);
if (flags & LocalFlags.WRITTENTO) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_WRITTENTO);
}
}
/** Inherits mutual flags and local wrap states from the specified flows (e.g. then with else). */
inheritMutual(left: Flow, right: Flow): void {
// categorical flags set in both arms
this.flags |= left.flags & right.flags & FlowFlags.ANY_CATEGORICAL;
this.set(left.flags & right.flags & FlowFlags.ANY_CATEGORICAL);
// conditional flags set in at least one arm
this.flags |= left.flags & FlowFlags.ANY_CONDITIONAL;
this.flags |= right.flags & FlowFlags.ANY_CONDITIONAL;
this.set(left.flags & FlowFlags.ANY_CONDITIONAL);
this.set(right.flags & FlowFlags.ANY_CONDITIONAL);
// 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]
);
// categorical local flags set in both arms / conditional local flags set in at least one arm
var leftLocalFlags = left.localFlags;
var numLeftLocalFlags = leftLocalFlags.length;
var rightLocalFlags = right.localFlags;
var numRightLocalFlags = rightLocalFlags.length;
var combinedFlags = new Array<LocalFlags>(max<i32>(numLeftLocalFlags, numRightLocalFlags));
for (let i = 0; i < numLeftLocalFlags; ++i) {
combinedFlags[i] = LocalFlags.join(
unchecked(leftLocalFlags[i]),
i < numRightLocalFlags
? unchecked(rightLocalFlags[i])
: 0
);
}
for (let i = numLeftLocalFlags; i < numRightLocalFlags; ++i) {
combinedFlags[i] = LocalFlags.join(
0,
unchecked(rightLocalFlags[i])
);
}
this.localFlags = combinedFlags;
}
/** Checks if an expression of the specified type is known to be non-null, even if the type might be nullable. */
isNonnull(expr: ExpressionRef, type: Type): bool {
if (!type.is(TypeFlags.NULLABLE)) return true;
// below, only teeLocal/getLocal are relevant because these are the only expressions that
// depend on a dynamic nullable state (flag = LocalFlags.NONNULL), while everything else
// has already been handled by the nullable type check above.
switch (getExpressionId(expr)) {
case ExpressionId.LocalSet: {
if (!isLocalTee(expr)) break;
let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)];
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
}
case ExpressionId.LocalGet: {
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
}
}
return false;
}
/** Updates local states to reflect that this branch is only taken when `expr` is true-ish. */
inheritNonnullIfTrue(expr: ExpressionRef): void {
// A: `expr` is true-ish -> Q: how did that happen?
switch (getExpressionId(expr)) {
case ExpressionId.LocalSet: {
if (!isLocalTee(expr)) break;
let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)];
this.setLocalFlag(local.index, LocalFlags.NONNULL);
this.inheritNonnullIfTrue(getLocalSetValue(expr)); // must have been true-ish as well
break;
}
case ExpressionId.LocalGet: {
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
this.setLocalFlag(local.index, LocalFlags.NONNULL);
break;
}
case ExpressionId.If: {
let ifFalse = getIfFalse(expr);
if (!ifFalse) break;
if (getExpressionId(ifFalse) == ExpressionId.Const) {
// Logical AND: (if (condition ifTrue 0))
// the only way this had become true is if condition and ifTrue are true
if (
(getExpressionType(ifFalse) == NativeType.I32 && getConstValueI32(ifFalse) == 0) ||
(getExpressionType(ifFalse) == NativeType.I64 && getConstValueI64Low(ifFalse) == 0 && getConstValueI64High(ifFalse) == 0)
) {
this.inheritNonnullIfTrue(getIfCondition(expr));
this.inheritNonnullIfTrue(getIfTrue(expr));
}
}
break;
}
case ExpressionId.Unary: {
switch (getUnaryOp(expr)) {
case UnaryOp.EqzI32:
case UnaryOp.EqzI64: {
this.inheritNonnullIfFalse(getUnaryValue(expr)); // !value -> value must have been false
break;
}
}
break;
}
case ExpressionId.Binary: {
switch (getBinaryOp(expr)) {
case BinaryOp.EqI32: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) != 0) {
this.inheritNonnullIfTrue(right); // TRUE == right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) != 0) {
this.inheritNonnullIfTrue(left); // left == TRUE -> left must have been true
}
break;
}
case BinaryOp.EqI64: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && (getConstValueI64Low(left) != 0 || getConstValueI64High(left) != 0)) {
this.inheritNonnullIfTrue(right); // TRUE == right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && (getConstValueI64Low(right) != 0 && getConstValueI64High(right) != 0)) {
this.inheritNonnullIfTrue(left); // left == TRUE -> left must have been true
}
break;
}
case BinaryOp.NeI32: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) == 0) {
this.inheritNonnullIfTrue(right); // FALSE != right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) == 0) {
this.inheritNonnullIfTrue(left); // left != FALSE -> left must have been true
}
break;
}
case BinaryOp.NeI64: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI64Low(left) == 0 && getConstValueI64High(left) == 0) {
this.inheritNonnullIfTrue(right); // FALSE != right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI64Low(right) == 0 && getConstValueI64High(right) == 0) {
this.inheritNonnullIfTrue(left); // left != FALSE -> left must have been true
}
break;
}
}
break;
}
case ExpressionId.Call: {
let name = getCallTarget(expr);
let program = this.parentFunction.program;
switch (name) {
case program.retainInstance.internalName: {
this.inheritNonnullIfTrue(getCallOperand(expr, 0));
break;
}
}
break;
}
}
}
/** Updates local states to reflect that this branch is only taken when `expr` is false-ish. */
inheritNonnullIfFalse(expr: ExpressionRef): void {
// A: `expr` is false-ish -> Q: how did that happen?
switch (getExpressionId(expr)) {
case ExpressionId.Unary: {
switch (getUnaryOp(expr)) {
case UnaryOp.EqzI32:
case UnaryOp.EqzI64: {
this.inheritNonnullIfTrue(getUnaryValue(expr)); // !value -> value must have been true
break;
}
}
break;
}
case ExpressionId.If: {
let ifTrue = getIfTrue(expr);
if (getExpressionId(ifTrue) == ExpressionId.Const) {
let ifFalse = getIfFalse(expr);
if (!ifFalse) break;
// Logical OR: (if (condition 1 ifFalse))
// the only way this had become false is if condition and ifFalse are false
if (
(getExpressionType(ifTrue) == NativeType.I32 && getConstValueI32(ifTrue) != 0) ||
(getExpressionType(ifTrue) == NativeType.I64 && (getConstValueI64Low(ifTrue) != 0 || getConstValueI64High(ifTrue) != 0))
) {
this.inheritNonnullIfFalse(getIfCondition(expr));
this.inheritNonnullIfFalse(getIfFalse(expr));
}
}
break;
}
case ExpressionId.Binary: {
switch (getBinaryOp(expr)) {
// remember: we want to know how the _entire_ expression became FALSE (!)
case BinaryOp.EqI32: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) == 0) {
this.inheritNonnullIfTrue(right); // FALSE == right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) == 0) {
this.inheritNonnullIfTrue(left); // left == FALSE -> left must have been true
}
break;
}
case BinaryOp.EqI64: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI64Low(left) == 0 && getConstValueI64High(left) == 0) {
this.inheritNonnullIfTrue(right); // FALSE == right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI64Low(right) == 0 && getConstValueI64High(right) == 0) {
this.inheritNonnullIfTrue(left); // left == FALSE -> left must have been true
}
break;
}
case BinaryOp.NeI32: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) != 0) {
this.inheritNonnullIfTrue(right); // TRUE != right -> right must have been true
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) != 0) {
this.inheritNonnullIfTrue(left); // left != TRUE -> left must have been true
}
break;
}
case BinaryOp.NeI64: {
let left = getBinaryLeft(expr);
let right = getBinaryRight(expr);
if (getExpressionId(left) == ExpressionId.Const && (getConstValueI64Low(left) != 0 || getConstValueI64High(left) != 0)) {
this.inheritNonnullIfTrue(right); // TRUE != right -> right must have been true for this to become false
} else if (getExpressionId(right) == ExpressionId.Const && (getConstValueI64Low(right) != 0 || getConstValueI64High(right) != 0)) {
this.inheritNonnullIfTrue(left); // left != TRUE -> left must have been true for this to become false
}
break;
}
}
break;
}
}
}
@ -523,22 +797,22 @@ export class Flow {
switch (getExpressionId(expr)) {
// overflows if the local isn't wrapped or the conversion does
case ExpressionId.GetLocal: {
let local = this.parentFunction.localsByIndex[getGetLocalIndex(expr)];
return !this.isLocalWrapped(local.index)
case ExpressionId.LocalGet: {
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
return !this.isLocalFlag(local.index, LocalFlags.WRAPPED, true)
|| canConversionOverflow(local.type, type);
}
// overflows if the value does
case ExpressionId.SetLocal: { // tee
assert(isTeeLocal(expr));
return this.canOverflow(getSetLocalValue(expr), type);
case ExpressionId.LocalSet: { // tee
assert(isLocalTee(expr));
return this.canOverflow(getLocalSetValue(expr), type);
}
// overflows if the conversion does (globals are wrapped on set)
case ExpressionId.GetGlobal: {
case ExpressionId.GlobalGet: {
// TODO: this is inefficient because it has to read a string
let global = assert(this.parentFunction.program.elementsByName.get(assert(getGetGlobalName(expr))));
let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr))));
assert(global.kind == ElementKind.GLOBAL);
return canConversionOverflow(assert((<Global>global).type), type);
}
@ -705,10 +979,11 @@ export class Flow {
// overflows if the conversion does
case ExpressionId.Load: {
let fromType: Type;
let signed = isLoadSigned(expr);
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; }
case 1: { fromType = signed ? Type.i8 : Type.u8; break; }
case 2: { fromType = signed ? Type.i16 : Type.u16; break; }
default: { fromType = signed ? Type.i32 : Type.u32; break; }
}
return canConversionOverflow(fromType, type);
}
@ -740,11 +1015,16 @@ export class Flow {
// overflows if the call does not return a wrapped value or the conversion does
case ExpressionId.Call: {
let program = this.parentFunction.program;
let instance = assert(program.instancesByName.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);
let instancesByName = program.instancesByName;
let instanceName = assert(getCallTarget(expr));
if (instancesByName.has(instanceName)) {
let instance = instancesByName.get(instanceName)!;
assert(instance.kind == ElementKind.FUNCTION);
let returnType = (<Function>instance).signature.returnType;
return !(<Function>instance).flow.is(FlowFlags.RETURNS_WRAPPED)
|| canConversionOverflow(returnType, type);
}
return false; // assume no overflow for builtins
}
// doesn't technically overflow
@ -752,6 +1032,16 @@ export class Flow {
}
return true;
}
toString(): string {
var levels = 0;
var parent = this.parent;
while (parent) {
parent = parent.parent;
++levels;
}
return "Flow(" + this.actualFunction + ")[" + levels.toString() + "]";
}
}
/** Tests if a conversion from one type to another can technically overflow. */
@ -760,3 +1050,24 @@ function canConversionOverflow(fromType: Type, toType: Type): bool {
|| fromType.size > toType.size
|| fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED);
}
/** Finds all indexes of locals used in the specified expression. */
export function findUsedLocals(expr: ExpressionRef, used: Set<i32> = new Set()): Set<i32> {
traverse(expr, used, findUsedLocalsVisit);
return used;
}
/** A visitor function for use with `traverse` that finds all indexes of used locals. */
function findUsedLocalsVisit(expr: ExpressionRef, used: Set<i32>): void {
switch (getExpressionId(expr)) {
case ExpressionId.LocalGet: {
used.add(getLocalGetIndex(expr));
break;
}
case ExpressionId.LocalSet: {
used.add(getLocalSetIndex(expr));
// fall-through for value
}
default: traverse(expr, used, findUsedLocalsVisit);
}
}

View File

@ -22,6 +22,16 @@ declare function _BinaryenTypeVec128(): BinaryenType;
declare function _BinaryenTypeUnreachable(): BinaryenType;
declare function _BinaryenTypeAuto(): BinaryenType;
declare type BinaryenFeatureFlags = u32;
declare function _BinaryenFeatureAtomics(): BinaryenFeatureFlags;
declare function _BinaryenFeatureMutableGlobals(): BinaryenFeatureFlags;
declare function _BinaryenFeatureNontrappingFPToInt(): BinaryenFeatureFlags;
declare function _BinaryenFeatureSIMD128(): BinaryenFeatureFlags;
declare function _BinaryenFeatureBulkMemory(): BinaryenFeatureFlags;
declare function _BinaryenFeatureSignExt(): BinaryenFeatureFlags;
declare function _BinaryenFeatureExceptionHandling(): BinaryenFeatureFlags;
declare type BinaryenExpressionId = i32;
declare function _BinaryenInvalidId(): BinaryenExpressionId;
@ -32,10 +42,10 @@ declare function _BinaryenBreakId(): BinaryenExpressionId;
declare function _BinaryenSwitchId(): BinaryenExpressionId;
declare function _BinaryenCallId(): BinaryenExpressionId;
declare function _BinaryenCallIndirectId(): BinaryenExpressionId;
declare function _BinaryenGetLocalId(): BinaryenExpressionId;
declare function _BinaryenSetLocalId(): BinaryenExpressionId;
declare function _BinaryenGetGlobalId(): BinaryenExpressionId;
declare function _BinaryenSetGlobalId(): BinaryenExpressionId;
declare function _BinaryenLocalGetId(): BinaryenExpressionId;
declare function _BinaryenLocalSetId(): BinaryenExpressionId;
declare function _BinaryenGlobalGetId(): BinaryenExpressionId;
declare function _BinaryenGlobalSetId(): BinaryenExpressionId;
declare function _BinaryenLoadId(): BinaryenExpressionId;
declare function _BinaryenStoreId(): BinaryenExpressionId;
declare function _BinaryenConstId(): BinaryenExpressionId;
@ -211,8 +221,8 @@ declare function _BinaryenGeFloat64(): BinaryenOp;
declare type BinaryenHostOp = BinaryenOp;
declare function _BinaryenCurrentMemory(): BinaryenHostOp;
declare function _BinaryenGrowMemory(): BinaryenHostOp;
declare function _BinaryenMemorySize(): BinaryenHostOp;
declare function _BinaryenMemoryGrow(): BinaryenHostOp;
declare type BinaryenAtomicRMWOp = BinaryenOp;
@ -370,11 +380,11 @@ declare function _BinaryenBreak(module: BinaryenModuleRef, name: usize, conditio
declare function _BinaryenSwitch(module: BinaryenModuleRef, names: usize, numNames: BinaryenIndex, defaultName: usize, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenCall(module: BinaryenModuleRef, target: usize, operands: usize, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenCallIndirect(module: BinaryenModuleRef, target: BinaryenExpressionRef, operands: usize, numOperands: BinaryenIndex, type: usize): BinaryenExpressionRef;
declare function _BinaryenGetLocal(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenSetLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenTeeLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: usize, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenSetGlobal(module: BinaryenModuleRef, name: usize, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLocalGet(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenLocalSet(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLocalTee(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenGlobalGet(module: BinaryenModuleRef, name: usize, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenGlobalSet(module: BinaryenModuleRef, name: usize, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLoad(module: BinaryenModuleRef, bytes: u32, signed: i8, offset: u32, align: u32, type: BinaryenType, ptr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenStore(module: BinaryenModuleRef, bytes: u32, offset: u32, align: u32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenConst(module: BinaryenModuleRef, value: usize): BinaryenExpressionRef;
@ -438,16 +448,16 @@ declare function _BinaryenCallIndirectGetTarget(expr: BinaryenExpressionRef): Bi
declare function _BinaryenCallIndirectGetNumOperands(expr: BinaryenExpressionRef): BinaryenIndex;
declare function _BinaryenCallIndirectGetOperand(expr: BinaryenExpressionRef, index: BinaryenIndex): BinaryenExpressionRef;
declare function _BinaryenGetLocalGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
declare function _BinaryenLocalGetGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
declare function _BinaryenSetLocalIsTee(expr: BinaryenExpressionRef): bool;
declare function _BinaryenSetLocalGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
declare function _BinaryenSetLocalGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLocalSetIsTee(expr: BinaryenExpressionRef): bool;
declare function _BinaryenLocalSetGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
declare function _BinaryenLocalSetGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenGetGlobalGetName(expr: BinaryenExpressionRef): usize;
declare function _BinaryenGlobalGetGetName(expr: BinaryenExpressionRef): usize;
declare function _BinaryenSetGlobalGetName(expr: BinaryenExpressionRef): usize;
declare function _BinaryenSetGlobalGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenGlobalSetGetName(expr: BinaryenExpressionRef): usize;
declare function _BinaryenGlobalSetGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenHostGetOp(expr: BinaryenExpressionRef): BinaryenOp;
declare function _BinaryenHostGetNameOperand(expr: BinaryenExpressionRef): usize;
@ -612,6 +622,8 @@ declare function _BinaryenModuleRead(input: usize, inputSize: usize): BinaryenMo
declare function _BinaryenModuleInterpret(module: BinaryenModuleRef): void;
declare function _BinaryenModuleAddDebugInfoFileName(module: BinaryenModuleRef, filename: usize): BinaryenIndex;
declare function _BinaryenModuleGetDebugInfoFileName(module: BinaryenModuleRef, index: BinaryenIndex): usize;
declare function _BinaryenModuleGetFeatures(module: BinaryenModuleRef): BinaryenFeatureFlags;
declare function _BinaryenModuleSetFeatures(module: BinaryenModuleRef, featureFlags: BinaryenFeatureFlags): void;
declare type BinaryenRelooperRef = usize;
declare type BinaryenRelooperBlockRef = usize;

View File

@ -3,39 +3,14 @@
* @module index
*//***/
import {
Compiler,
Options,
Target,
Feature
} from "./compiler";
import {
Decompiler
} from "./decompiler";
import {
IDLBuilder,
TSDBuilder
} from "./definitions";
import {
DiagnosticMessage,
DiagnosticCategory,
formatDiagnosticMessage
} from "./diagnostics";
import {
Module
} from "./module";
import {
Parser
} from "./parser";
import {
Program
} from "./program";
import { Target, Feature } from "./common";
import { Compiler, Options } from "./compiler";
import { Decompiler } from "./decompiler";
import { IDLBuilder, TSDBuilder } from "./definitions";
import { DiagnosticMessage, DiagnosticCategory, formatDiagnosticMessage } from "./diagnostics";
import { Module } from "./module";
import { Parser } from "./parser";
import { Program } from "./program";
/** Parses a source file. If `parser` has been omitted a new one is created. */
export function parseFile(text: string, path: string, isEntry: bool = false,
@ -124,6 +99,11 @@ export function setGlobalAlias(options: Options, name: string, alias: string): v
globalAliases.set(name, alias);
}
/** Sets the `explicitStart` option. */
export function setExplicitStart(options: Options, explicitStart: bool): void {
options.explicitStart = explicitStart;
}
/** Sign extension operations. */
export const FEATURE_SIGN_EXTENSION = Feature.SIGN_EXTENSION;
/** Mutable global imports and exports. */
@ -173,6 +153,32 @@ export function buildTSD(program: Program): string {
return TSDBuilder.build(program);
}
/** Builds a JSON file of a program's runtime type information. */
export function buildRTTI(program: Program): string {
var sb = new Array<string>();
sb.push("{\n \"names\": [\n");
for (let cls of program.managedClasses.values()) {
sb.push(" \"");
sb.push(cls.internalName);
sb.push("\",\n");
}
sb.push(" ],\n \"base\": [\n");
for (let cls of program.managedClasses.values()) {
let base = cls.base;
sb.push(" ");
sb.push(base ? base.id.toString() : "0");
sb.push(",\n");
}
sb.push(" ],\n \"flags\": [\n");
for (let cls of program.managedClasses.values()) {
sb.push(" ");
sb.push(cls.rttiFlags.toString());
sb.push(",\n");
}
sb.push(" ]\n}\n");
return sb.join("");
}
/** Prefix indicating a library file. */
export { LIBRARY_PREFIX } from "./common";

View File

@ -3,9 +3,7 @@
* @module module
*//***/
import {
Target
} from "./compiler";
import { Target } from "./common";
export type ModuleRef = usize;
export type FunctionTypeRef = usize;
@ -29,6 +27,16 @@ export enum NativeType {
Auto = _BinaryenTypeAuto()
}
export enum FeatureFlags {
Atomics = _BinaryenFeatureAtomics(),
MutableGloabls = _BinaryenFeatureMutableGlobals(),
NontrappingFPToInt = _BinaryenFeatureNontrappingFPToInt(),
SIMD128 = _BinaryenFeatureSIMD128(),
BulkMemory = _BinaryenFeatureBulkMemory(),
SignExt = _BinaryenFeatureSignExt(),
ExceptionHandling = _BinaryenFeatureExceptionHandling()
}
export enum ExpressionId {
Invalid = _BinaryenInvalidId(),
Block = _BinaryenBlockId(),
@ -38,10 +46,10 @@ export enum ExpressionId {
Switch = _BinaryenSwitchId(),
Call = _BinaryenCallId(),
CallIndirect = _BinaryenCallIndirectId(),
GetLocal = _BinaryenGetLocalId(),
SetLocal = _BinaryenSetLocalId(),
GetGlobal = _BinaryenGetGlobalId(),
SetGlobal = _BinaryenSetGlobalId(),
LocalGet = _BinaryenLocalGetId(),
LocalSet = _BinaryenLocalSetId(),
GlobalGet = _BinaryenGlobalGetId(),
GlobalSet = _BinaryenGlobalSetId(),
Load = _BinaryenLoadId(),
Store = _BinaryenStoreId(),
Const = _BinaryenConstId(),
@ -328,8 +336,8 @@ export enum BinaryOp {
}
export enum HostOp {
CurrentMemory = _BinaryenCurrentMemory(),
GrowMemory = _BinaryenGrowMemory(),
MemorySize = _BinaryenMemorySize(),
MemoryGrow = _BinaryenMemoryGrow(),
}
export enum AtomicRMWOp {
@ -451,31 +459,31 @@ export class Module {
// constants
createI32(value: i32): ExpressionRef {
i32(value: i32): ExpressionRef {
var out = this.lit;
_BinaryenLiteralInt32(out, value);
return _BinaryenConst(this.ref, out);
}
createI64(valueLow: i32, valueHigh: i32 = 0): ExpressionRef {
i64(valueLow: i32, valueHigh: i32 = 0): ExpressionRef {
var out = this.lit;
_BinaryenLiteralInt64(out, valueLow, valueHigh);
return _BinaryenConst(this.ref, out);
}
createF32(value: f32): ExpressionRef {
f32(value: f32): ExpressionRef {
var out = this.lit;
_BinaryenLiteralFloat32(out, value);
return _BinaryenConst(this.ref, out);
}
createF64(value: f64): ExpressionRef {
f64(value: f64): ExpressionRef {
var out = this.lit;
_BinaryenLiteralFloat64(out, value);
return _BinaryenConst(this.ref, out);
}
createV128(bytes: Uint8Array): ExpressionRef {
v128(bytes: Uint8Array): ExpressionRef {
assert(bytes.length == 16);
var out = this.lit;
for (let i = 0; i < 16; ++i) store<u8>(out + i, bytes[i]);
@ -485,14 +493,14 @@ export class Module {
// expressions
createUnary(
unary(
op: UnaryOp,
expr: ExpressionRef
): ExpressionRef {
return _BinaryenUnary(this.ref, op, expr);
}
createBinary(
binary(
op: BinaryOp,
left: ExpressionRef,
right: ExpressionRef
@ -500,7 +508,7 @@ export class Module {
return _BinaryenBinary(this.ref, op, left, right);
}
createHost(
host(
op: HostOp,
name: string | null = null,
operands: ExpressionRef[] | null = null
@ -514,29 +522,29 @@ export class Module {
}
}
createGetLocal(
local_get(
index: i32,
type: NativeType
): ExpressionRef {
return _BinaryenGetLocal(this.ref, index, type);
return _BinaryenLocalGet(this.ref, index, type);
}
createTeeLocal(
local_tee(
index: i32,
value: ExpressionRef
): ExpressionRef {
return _BinaryenTeeLocal(this.ref, index, value);
return _BinaryenLocalTee(this.ref, index, value);
}
createGetGlobal(
global_get(
name: string,
type: NativeType
): ExpressionRef {
var cStr = this.allocStringCached(name);
return _BinaryenGetGlobal(this.ref, cStr, type);
return _BinaryenGlobalGet(this.ref, cStr, type);
}
createLoad(
load(
bytes: Index,
signed: bool,
ptr: ExpressionRef,
@ -547,7 +555,7 @@ export class Module {
return _BinaryenLoad(this.ref, bytes, signed ? 1 : 0, offset, align, type, ptr);
}
createStore(
store(
bytes: Index,
ptr: ExpressionRef,
value: ExpressionRef,
@ -555,10 +563,11 @@ export class Module {
offset: Index = 0,
align: Index = bytes // naturally aligned by default
): ExpressionRef {
if (type < NativeType.None || type > NativeType.V128) throw new Error("here: " + type);
return _BinaryenStore(this.ref, bytes, offset, align, ptr, value, type);
}
createAtomicLoad(
atomic_load(
bytes: Index,
ptr: ExpressionRef,
type: NativeType,
@ -567,7 +576,7 @@ export class Module {
return _BinaryenAtomicLoad(this.ref, bytes, offset, type, ptr);
}
createAtomicStore(
atomic_store(
bytes: Index,
ptr: ExpressionRef,
value: ExpressionRef,
@ -577,7 +586,7 @@ export class Module {
return _BinaryenAtomicStore(this.ref, bytes, offset, ptr, value, type);
}
createAtomicRMW(
atomic_rmw(
op: AtomicRMWOp,
bytes: Index,
offset: Index,
@ -588,7 +597,7 @@ export class Module {
return _BinaryenAtomicRMW(this.ref, op, bytes, offset, ptr, value, type);
}
createAtomicCmpxchg(
atomic_cmpxchg(
bytes: Index,
offset: Index,
ptr: ExpressionRef,
@ -599,7 +608,7 @@ export class Module {
return _BinaryenAtomicCmpxchg(this.ref, bytes, offset, ptr, expected, replacement, type);
}
createAtomicWait(
atomic_wait(
ptr: ExpressionRef,
expected: ExpressionRef,
timeout: ExpressionRef,
@ -608,7 +617,7 @@ export class Module {
return _BinaryenAtomicWait(this.ref, ptr, expected, timeout, expectedType);
}
createAtomicNotify(
atomic_notify(
ptr: ExpressionRef,
notifyCount: ExpressionRef
): ExpressionRef {
@ -617,22 +626,22 @@ export class Module {
// statements
createSetLocal(
local_set(
index: Index,
value: ExpressionRef
): ExpressionRef {
return _BinaryenSetLocal(this.ref, index, value);
return _BinaryenLocalSet(this.ref, index, value);
}
createSetGlobal(
global_set(
name: string,
value: ExpressionRef
): ExpressionRef {
var cStr = this.allocStringCached(name);
return _BinaryenSetGlobal(this.ref, cStr, value);
return _BinaryenGlobalSet(this.ref, cStr, value);
}
createBlock(
block(
label: string | null,
children: ExpressionRef[],
type: NativeType = NativeType.None
@ -646,7 +655,7 @@ export class Module {
}
}
createBreak(
br(
label: string | null,
condition: ExpressionRef = 0,
value: ExpressionRef = 0
@ -655,13 +664,13 @@ export class Module {
return _BinaryenBreak(this.ref, cStr, condition, value);
}
createDrop(
drop(
expression: ExpressionRef
): ExpressionRef {
return _BinaryenDrop(this.ref, expression);
}
createLoop(
loop(
label: string | null,
body: ExpressionRef
): ExpressionRef {
@ -669,7 +678,7 @@ export class Module {
return _BinaryenLoop(this.ref, cStr, body);
}
createIf(
if(
condition: ExpressionRef,
ifTrue: ExpressionRef,
ifFalse: ExpressionRef = 0
@ -677,17 +686,17 @@ export class Module {
return _BinaryenIf(this.ref, condition, ifTrue, ifFalse);
}
createNop(): ExpressionRef {
nop(): ExpressionRef {
return _BinaryenNop(this.ref);
}
createReturn(
return(
expression: ExpressionRef = 0
): ExpressionRef {
return _BinaryenReturn(this.ref, expression);
}
createSelect(
select(
ifTrue: ExpressionRef,
ifFalse: ExpressionRef,
condition: ExpressionRef
@ -695,7 +704,7 @@ export class Module {
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
}
createSwitch(
switch(
names: string[],
defaultName: string | null,
condition: ExpressionRef,
@ -715,7 +724,7 @@ export class Module {
}
}
createCall(
call(
target: string,
operands: ExpressionRef[] | null,
returnType: NativeType
@ -729,7 +738,7 @@ export class Module {
}
}
createCallIndirect(
call_indirect(
index: ExpressionRef,
operands: ExpressionRef[] | null,
typeName: string
@ -743,13 +752,13 @@ export class Module {
}
}
createUnreachable(): ExpressionRef {
unreachable(): ExpressionRef {
return _BinaryenUnreachable(this.ref);
}
// bulk memory
createMemoryCopy(
memory_copy(
dest: ExpressionRef,
source: ExpressionRef,
size: ExpressionRef
@ -757,7 +766,7 @@ export class Module {
return _BinaryenMemoryCopy(this.ref, dest, source, size);
}
createMemoryFill(
memory_fill(
dest: ExpressionRef,
value: ExpressionRef,
size: ExpressionRef
@ -767,7 +776,7 @@ export class Module {
// simd
createSIMDExtract(
simd_extract(
op: SIMDExtractOp,
vec: ExpressionRef,
idx: u8
@ -775,7 +784,7 @@ export class Module {
return _BinaryenSIMDExtract(this.ref, op, vec, idx);
}
createSIMDReplace(
simd_replace(
op: SIMDReplaceOp,
vec: ExpressionRef,
idx: u8,
@ -784,7 +793,7 @@ export class Module {
return _BinaryenSIMDReplace(this.ref, op, vec, idx, value);
}
createSIMDShuffle(
simd_shuffle(
vec1: ExpressionRef,
vec2: ExpressionRef,
mask: Uint8Array
@ -798,7 +807,7 @@ export class Module {
}
}
createSIMDBitselect(
simd_bitselect(
vec1: ExpressionRef,
vec2: ExpressionRef,
cond: ExpressionRef
@ -806,7 +815,7 @@ export class Module {
return _BinaryenSIMDBitselect(this.ref, vec1, vec2, cond);
}
createSIMDShift(
simd_shift(
op: SIMDShiftOp,
vec: ExpressionRef,
shift: ExpressionRef
@ -985,8 +994,8 @@ export class Module {
segs[i] = allocU8Array(buffer);
psvs[i] = 0; // no passive segments currently
offs[i] = target == Target.WASM64
? this.createI64(i64_low(offset), i64_high(offset))
: this.createI32(i64_low(offset));
? this.i64(i64_low(offset), i64_high(offset))
: this.i32(i64_low(offset));
sizs[i] = buffer.length;
}
var cArr1 = allocI32Array(segs);
@ -1046,6 +1055,14 @@ export class Module {
_BinaryenSetDebugInfo(on);
}
getFeatures(): BinaryenFeatureFlags {
return _BinaryenModuleGetFeatures(this.ref);
}
setFeatures(featureFlags: BinaryenFeatureFlags): void {
_BinaryenModuleSetFeatures(this.ref, featureFlags);
}
optimize(func: FunctionRef = 0): void {
if (func) {
_BinaryenFunctionOptimize(func, this.ref);
@ -1181,19 +1198,19 @@ export class Module {
case ExpressionId.Const: {
switch (_BinaryenExpressionGetType(expr)) {
case NativeType.I32: {
return this.createI32(_BinaryenConstGetValueI32(expr));
return this.i32(_BinaryenConstGetValueI32(expr));
}
case NativeType.I64: {
return this.createI64(
return this.i64(
_BinaryenConstGetValueI64Low(expr),
_BinaryenConstGetValueI64High(expr)
);
}
case NativeType.F32: {
return this.createF32(_BinaryenConstGetValueF32(expr));
return this.f32(_BinaryenConstGetValueF32(expr));
}
case NativeType.F64: {
return this.createF64(_BinaryenConstGetValueF64(expr));
return this.f64(_BinaryenConstGetValueF64(expr));
}
case NativeType.V128: {
// TODO
@ -1204,16 +1221,16 @@ export class Module {
}
}
}
case ExpressionId.GetLocal: {
return _BinaryenGetLocal(this.ref,
_BinaryenGetLocalGetIndex(expr),
case ExpressionId.LocalGet: {
return _BinaryenLocalGet(this.ref,
_BinaryenLocalGetGetIndex(expr),
_BinaryenExpressionGetType(expr)
);
}
case ExpressionId.GetGlobal: {
let globalName = _BinaryenGetGlobalGetName(expr);
case ExpressionId.GlobalGet: {
let globalName = _BinaryenGlobalGetGetName(expr);
if (!globalName) break;
return _BinaryenGetGlobal(this.ref, globalName, _BinaryenExpressionGetType(expr));
return _BinaryenGlobalGet(this.ref, globalName, _BinaryenExpressionGetType(expr));
}
case ExpressionId.Load: {
if (!(nested1 = this.cloneExpression(_BinaryenLoadGetPtr(expr), noSideEffects, maxDepth))) {
@ -1312,24 +1329,24 @@ export function getConstValueF64(expr: ExpressionRef): f32 {
return _BinaryenConstGetValueF64(expr);
}
export function getGetLocalIndex(expr: ExpressionRef): Index {
return _BinaryenGetLocalGetIndex(expr);
export function getLocalGetIndex(expr: ExpressionRef): Index {
return _BinaryenLocalGetGetIndex(expr);
}
export function getSetLocalIndex(expr: ExpressionRef): Index {
return _BinaryenSetLocalGetIndex(expr);
export function getLocalSetIndex(expr: ExpressionRef): Index {
return _BinaryenLocalSetGetIndex(expr);
}
export function getSetLocalValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenSetLocalGetValue(expr);
export function getLocalSetValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenLocalSetGetValue(expr);
}
export function isTeeLocal(expr: ExpressionRef): bool {
return _BinaryenSetLocalIsTee(expr);
export function isLocalTee(expr: ExpressionRef): bool {
return _BinaryenLocalSetIsTee(expr);
}
export function getGetGlobalName(expr: ExpressionRef): string | null {
return readString(_BinaryenGetGlobalGetName(expr));
export function getGlobalGetName(expr: ExpressionRef): string | null {
return readString(_BinaryenGlobalGetGetName(expr));
}
export function getBinaryOp(expr: ExpressionRef): BinaryOp {
@ -1448,6 +1465,14 @@ export function getCallTarget(expr: ExpressionRef): string | null {
return readString(_BinaryenCallGetTarget(expr));
}
export function getCallOperandCount(expr: ExpressionRef): i32 {
return _BinaryenCallGetNumOperands(expr);
}
export function getCallOperand(expr: ExpressionRef, index: Index): ExpressionRef {
return _BinaryenCallGetOperand(expr, index);
}
export function getHostOp(expr: ExpressionRef): ExpressionRef {
return _BinaryenHostGetOp(expr);
}
@ -1765,3 +1790,177 @@ export function needsExplicitUnreachable(expr: ExpressionRef): bool {
}
return true;
}
/** Traverses all expression members of an expression, calling the given visitor. */
export function traverse<T>(expr: ExpressionRef, data: T, visit: (expr: ExpressionRef, data: T) => void): bool {
switch (getExpressionId(expr)) {
case ExpressionId.Block: {
for (let i = 0, n = _BinaryenBlockGetNumChildren(expr); i < n; ++i) {
visit(_BinaryenBlockGetChild(expr, i), data);
}
break;
}
case ExpressionId.If: {
visit(_BinaryenIfGetCondition(expr), data);
visit(_BinaryenIfGetIfTrue(expr), data);
let ifFalse = _BinaryenIfGetIfFalse(expr);
if (ifFalse) visit(ifFalse, data);
break;
}
case ExpressionId.Loop: {
visit(_BinaryenLoopGetBody(expr), data);
break;
}
case ExpressionId.Break: {
let condition = _BinaryenBreakGetCondition(expr);
if (condition) visit(condition, data);
break;
}
case ExpressionId.Switch: {
visit(_BinaryenSwitchGetCondition(expr), data);
break;
}
case ExpressionId.Call: {
for (let i = 0, n = _BinaryenCallGetNumOperands(expr); i < n; ++i) {
visit(_BinaryenCallGetOperand(expr, i), data);
}
break;
}
case ExpressionId.CallIndirect: {
for (let i = 0, n = _BinaryenCallIndirectGetNumOperands(expr); i < n; ++i) {
visit(_BinaryenCallIndirectGetOperand(expr, i), data);
}
break;
}
case ExpressionId.LocalGet: {
break;
}
case ExpressionId.LocalSet: {
visit(_BinaryenLocalSetGetValue(expr), data);
break;
}
case ExpressionId.GlobalGet: {
break;
}
case ExpressionId.GlobalSet: {
visit(_BinaryenGlobalSetGetValue(expr), data);
break;
}
case ExpressionId.Load: {
visit(_BinaryenLoadGetPtr(expr), data);
break;
}
case ExpressionId.Store: {
visit(_BinaryenStoreGetPtr(expr), data);
visit(_BinaryenStoreGetValue(expr), data);
break;
}
case ExpressionId.AtomicRMW: {
visit(_BinaryenAtomicRMWGetPtr(expr), data);
visit(_BinaryenAtomicRMWGetValue(expr), data);
break;
}
case ExpressionId.AtomicCmpxchg: {
visit(_BinaryenAtomicCmpxchgGetPtr(expr), data);
visit(_BinaryenAtomicCmpxchgGetExpected(expr), data);
visit(_BinaryenAtomicCmpxchgGetReplacement(expr), data);
break;
}
case ExpressionId.AtomicWait: {
visit(_BinaryenAtomicWaitGetPtr(expr), data);
visit(_BinaryenAtomicWaitGetExpected(expr), data);
visit(_BinaryenAtomicWaitGetTimeout(expr), data);
break;
}
case ExpressionId.AtomicNotify: {
visit(_BinaryenAtomicNotifyGetPtr(expr), data);
break;
}
case ExpressionId.SIMDExtract: {
visit(_BinaryenSIMDExtractGetVec(expr), data);
break;
}
case ExpressionId.SIMDReplace: {
visit(_BinaryenSIMDReplaceGetVec(expr), data);
visit(_BinaryenSIMDReplaceGetValue(expr), data);
break;
}
case ExpressionId.SIMDShuffle: {
visit(_BinaryenSIMDShuffleGetLeft(expr), data);
visit(_BinaryenSIMDShuffleGetRight(expr), data);
break;
}
case ExpressionId.SIMDBitselect: {
visit(_BinaryenSIMDBitselectGetLeft(expr), data);
visit(_BinaryenSIMDBitselectGetRight(expr), data);
visit(_BinaryenSIMDBitselectGetCond(expr), data);
break;
}
case ExpressionId.SIMDShift: {
visit(_BinaryenSIMDShiftGetVec(expr), data);
visit(_BinaryenSIMDShiftGetShift(expr), data);
break;
}
case ExpressionId.MemoryInit: {
visit(_BinaryenMemoryInitGetDest(expr), data);
visit(_BinaryenMemoryInitGetOffset(expr), data);
visit(_BinaryenMemoryInitGetSize(expr), data);
break;
}
case ExpressionId.DataDrop: {
break;
}
case ExpressionId.MemoryCopy: {
visit(_BinaryenMemoryCopyGetDest(expr), data);
visit(_BinaryenMemoryCopyGetSource(expr), data);
visit(_BinaryenMemoryCopyGetSize(expr), data);
break;
}
case ExpressionId.MemoryFill: {
visit(_BinaryenMemoryFillGetDest(expr), data);
visit(_BinaryenMemoryFillGetValue(expr), data);
visit(_BinaryenMemoryFillGetSize(expr), data);
break;
}
case ExpressionId.Const: {
break;
}
case ExpressionId.Unary: {
visit(_BinaryenUnaryGetValue(expr), data);
break;
}
case ExpressionId.Binary: {
visit(_BinaryenBinaryGetLeft(expr), data);
visit(_BinaryenBinaryGetRight(expr), data);
break;
}
case ExpressionId.Select: {
visit(_BinaryenSelectGetIfTrue(expr), data);
visit(_BinaryenSelectGetIfFalse(expr), data);
visit(_BinaryenSelectGetCondition(expr), data);
break;
}
case ExpressionId.Drop: {
visit(_BinaryenDropGetValue(expr), data);
break;
}
case ExpressionId.Return: {
visit(_BinaryenReturnGetValue(expr), data);
break;
}
case ExpressionId.Host: {
for (let i = 0, n = _BinaryenHostGetNumOperands(expr); i < n; ++i) {
visit(_BinaryenHostGetOperand(expr, i), data);
}
break;
}
case ExpressionId.Nop: {
break;
}
case ExpressionId.Unreachable: {
break;
}
default: assert(false);
}
return true;
}

View File

@ -171,31 +171,34 @@ export class Parser extends DiagnosticEmitter {
// check modifiers
var exportStart: i32 = 0;
var exportEnd: i32 = 0;
var defaultStart: i32 = 0;
var defaultEnd: i32 = 0;
if (tn.skip(Token.EXPORT)) {
if (tn.skip(Token.DEFAULT)) {
this.error(
DiagnosticCode.Operation_not_supported,
tn.range()
);
}
if (startPos < 0) startPos = tn.tokenPos;
flags |= CommonFlags.EXPORT;
exportStart = tn.tokenPos;
exportEnd = tn.pos;
if (tn.skip(Token.DEFAULT)) {
defaultStart = tn.tokenPos;
defaultEnd = tn.pos;
}
}
var declareStart: i32 = 0;
var declareEnd: i32 = 0;
var contextIsAmbient = namespace != null && namespace.is(CommonFlags.AMBIENT);
if (tn.skip(Token.DECLARE)) {
if (startPos < 0) startPos = tn.tokenPos;
if (contextIsAmbient) {
this.error(
DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context,
tn.range()
); // recoverable
} else {
if (startPos < 0) startPos = tn.tokenPos;
declareStart = startPos;
declareEnd = tn.pos;
flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT;
}
flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT;
} else if (contextIsAmbient) {
flags |= CommonFlags.AMBIENT;
}
@ -297,7 +300,18 @@ export class Parser extends DiagnosticEmitter {
// handle plain exports
if (flags & CommonFlags.EXPORT) {
statement = this.parseExport(tn, startPos, (flags & CommonFlags.DECLARE) != 0);
if (defaultEnd && tn.skipIdentifier(IdentifierHandling.PREFER)) {
if (declareEnd) {
this.error(
DiagnosticCode.An_export_assignment_cannot_have_modifiers,
tn.range(declareStart, declareEnd)
);
}
statement = this.parseExportDefaultAlias(tn, startPos, defaultStart, defaultEnd);
defaultStart = defaultEnd = 0; // consume
} else {
statement = this.parseExport(tn, startPos, (flags & CommonFlags.DECLARE) != 0);
}
// handle non-declaration statements
} else {
@ -330,6 +344,25 @@ export class Parser extends DiagnosticEmitter {
);
}
}
// check if this an `export default` declaration
if (defaultEnd && statement !== null) {
switch (statement.kind) {
case NodeKind.ENUMDECLARATION:
case NodeKind.FUNCTIONDECLARATION:
case NodeKind.CLASSDECLARATION:
case NodeKind.INTERFACEDECLARATION:
case NodeKind.NAMESPACEDECLARATION: {
return Node.createExportDefaultStatement(<DeclarationStatement>statement, tn.range(startPos, tn.pos));
}
default: {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
tn.range(defaultStart, defaultEnd), "default"
);
}
}
}
return statement;
}
@ -1588,6 +1621,7 @@ export class Parser extends DiagnosticEmitter {
}
} while (!tn.skip(Token.CLOSEBRACE));
}
declaration.range.end = tn.pos;
return declaration;
}
@ -1639,6 +1673,7 @@ export class Parser extends DiagnosticEmitter {
}
} while (!tn.skip(Token.CLOSEBRACE));
}
declaration.range.end = tn.pos;
return Node.createClassExpression(declaration);
}
@ -2162,7 +2197,7 @@ export class Parser extends DiagnosticEmitter {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.OPENBRACE)) {
let members = new Array<Statement>();
let ns = Node.createNamespaceDeclaration(
let declaration = Node.createNamespaceDeclaration(
identifier,
members,
decorators,
@ -2170,7 +2205,7 @@ export class Parser extends DiagnosticEmitter {
tn.range(startPos, tn.pos)
);
while (!tn.skip(Token.CLOSEBRACE)) {
let member = this.parseTopLevelStatement(tn, ns);
let member = this.parseTopLevelStatement(tn, declaration);
if (member) members.push(member);
else {
this.skipStatement(tn);
@ -2183,8 +2218,9 @@ export class Parser extends DiagnosticEmitter {
}
}
}
declaration.range.end = tn.pos;
tn.skip(Token.SEMICOLON);
return ns;
return declaration;
} else {
this.error(
DiagnosticCode._0_expected,
@ -2318,6 +2354,28 @@ export class Parser extends DiagnosticEmitter {
return null;
}
parseExportDefaultAlias(
tn: Tokenizer,
startPos: i32,
defaultStart: i32,
defaultEnd: i32
): ExportStatement {
// at 'export' 'default': [Known-To-Be-]Identifier
var name = tn.readIdentifier();
var range = tn.range();
var ret = Node.createExportStatement([
Node.createExportMember(
Node.createIdentifierExpression(name, range),
Node.createIdentifierExpression("default", tn.range(defaultStart, defaultEnd)),
range
)
], null, false, tn.range(startPos, tn.pos));
tn.skip(Token.SEMICOLON);
return ret;
}
parseImport(
tn: Tokenizer
): ImportStatement | null {
@ -2330,7 +2388,7 @@ export class Parser extends DiagnosticEmitter {
var members: ImportDeclaration[] | null = null;
var namespaceName: IdentifierExpression | null = null;
var skipFrom = false;
if (tn.skip(Token.OPENBRACE)) {
if (tn.skip(Token.OPENBRACE)) { // import { ... } from "file"
members = new Array();
while (!tn.skip(Token.CLOSEBRACE)) {
let member = this.parseImportDeclaration(tn);
@ -2348,7 +2406,7 @@ export class Parser extends DiagnosticEmitter {
}
}
}
} else if (tn.skip(Token.ASTERISK)) {
} else if (tn.skip(Token.ASTERISK)) { // import * from "file"
if (tn.skip(Token.AS)) {
if (tn.skipIdentifier()) {
namespaceName = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
@ -2366,7 +2424,25 @@ export class Parser extends DiagnosticEmitter {
);
return null;
}
} else {
} else if (tn.skip(Token.IDENTIFIER, IdentifierHandling.PREFER)) { // import Name from "file"
let name = tn.readIdentifier();
let range = tn.range();
members = [
Node.createImportDeclaration(
Node.createIdentifierExpression("default", range),
Node.createIdentifierExpression(name, range),
range
)
];
if (tn.skip(Token.COMMA)) {
// TODO: default + star, default + members
this.error(
DiagnosticCode.Operation_not_supported,
tn.range()
);
return null;
}
} else { // import "file"
skipFrom = true;
}

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,10 @@ import {
Token
} from "./tokenizer";
import {
BuiltinSymbols
} from "./builtins";
/** Indicates whether errors are reported or not. */
export enum ReportMode {
/** Report errors. */
@ -368,12 +372,26 @@ export class Resolver extends DiagnosticEmitter {
);
// recoverable
}
return this.resolveType(
let type = this.resolveType(
(<TypeDefinition>element).typeNode,
element,
contextualTypeArguments,
reportMode
);
if (!type) return null;
if (node.isNullable) {
if (!type.is(TypeFlags.REFERENCE)) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Basic_type_0_cannot_be_nullable,
typeNode.name.range, typeName.identifier.text
);
}
} else {
return type.asNullable();
}
}
return type;
}
if (reportMode == ReportMode.REPORT) {
this.error(
@ -438,7 +456,7 @@ export class Resolver extends DiagnosticEmitter {
): Type[] | null {
var minParameterCount = 0;
var maxParameterCount = 0;
for (let i = 0; i < typeParameters.length; ++i) {
for (let i = 0, k = typeParameters.length; i < k; ++i) {
if (!typeParameters[i].defaultType) ++minParameterCount;
++maxParameterCount;
}
@ -610,25 +628,40 @@ export class Resolver extends DiagnosticEmitter {
case ElementKind.CLASS: { // property access on element access?
let elementExpression = this.currentElementExpression;
if (elementExpression) {
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)) {
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (!arrayType) {
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 arrayType = indexedGet.signature.returnType;
// }
if (!(target = arrayType.classReference)) {
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.property.range, propertyName, returnType.toString()
propertyAccess.property.range, propertyName, arrayType.toString()
);
return null;
}
}
break;
}
case ElementKind.FUNCTION_PROTOTYPE: { // function Symbol() + type Symbol = _Symbol
let shadowType = target.shadowType;
if (shadowType) {
if (!shadowType.is(CommonFlags.RESOLVED)) {
let resolvedType = this.resolveType(shadowType.typeNode, shadowType.parent, null, reportMode);
if (resolvedType) shadowType.setType(resolvedType);
}
let classReference = shadowType.type.classReference;
if (classReference) target = classReference.prototype;
break;
}
}
}
// Look up the member within
@ -672,6 +705,7 @@ export class Resolver extends DiagnosticEmitter {
break;
}
}
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.property.range, propertyName, target.internalName
@ -706,19 +740,22 @@ export class Resolver extends DiagnosticEmitter {
break;
}
case ElementKind.CLASS: {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
if (!indexedGet) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
elementAccess.range, (<Class>target).internalName
);
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
// if (!arrayType) {
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
if (!indexedGet) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
elementAccess.range, (<Class>target).internalName
);
}
return null;
}
return null;
}
let arrayType = indexedGet.signature.returnType;
// }
if (targetExpression.kind == NodeKind.ELEMENTACCESS) { // nested element access
let returnType = indexedGet.signature.returnType;
if (target = returnType.classReference) {
if (target = arrayType.classReference) {
this.currentThisExpression = targetExpression;
this.currentElementExpression = elementAccess.elementExpression;
return target;
@ -1189,6 +1226,14 @@ export class Resolver extends DiagnosticEmitter {
);
if (!target) return null;
if (target.kind == ElementKind.FUNCTION_PROTOTYPE) {
// `unchecked(expr: *): *` is special
if (
(<FunctionPrototype>target).internalName == BuiltinSymbols.unchecked &&
expression.arguments.length > 0
) {
return this.resolveExpression(expression.arguments[0], flow, contextualType, reportMode);
}
// otherwise resolve normally
let instance = this.resolveFunctionInclTypeArguments(
<FunctionPrototype>target,
expression.typeArguments,

View File

@ -350,6 +350,7 @@ export function tokenIsAlsoIdentifier(token: Token): bool {
case Token.FROM:
case Token.FOR:
case Token.GET:
case Token.INSTANCEOF:
case Token.IS:
case Token.KEYOF:
case Token.MODULE:
@ -446,6 +447,7 @@ export class Range {
get atStart(): Range {
return new Range(this.source, this.start, this.start);
}
get atEnd(): Range {
return new Range(this.source, this.end, this.end);
}
@ -504,13 +506,14 @@ export class Tokenizer extends DiagnosticEmitter {
this.source = source;
this.pos = 0;
this.end = source.text.length;
this.diagnostics = diagnostics ? diagnostics : new Array();
this.diagnostics = diagnostics || new Array();
var end = this.end;
var text = source.text;
// skip bom
if (
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) == CharCode.BYTEORDERMARK
) {
++this.pos;
@ -518,13 +521,13 @@ export class Tokenizer extends DiagnosticEmitter {
// skip shebang
if (
this.pos + 1 < this.end &&
this.pos + 1 < end &&
text.charCodeAt(this.pos) == CharCode.HASH &&
text.charCodeAt(this.pos + 1) == CharCode.EXCLAMATION
) {
this.pos += 2;
while (
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) != CharCode.LINEFEED
) {
++this.pos;
@ -542,14 +545,15 @@ export class Tokenizer extends DiagnosticEmitter {
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
maxTokenLength: i32 = i32.MAX_VALUE
): Token {
var end = this.end;
var text = this.source.text;
while (this.pos < this.end) {
while (this.pos < end) {
this.tokenPos = this.pos;
let c = text.charCodeAt(this.pos);
switch (c) {
case CharCode.CARRIAGERETURN: {
if (!(
++this.pos < this.end &&
++this.pos < end &&
text.charCodeAt(this.pos) == CharCode.LINEFEED
)) break;
// otherwise fall-through
@ -565,12 +569,12 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.EXCLAMATION: {
++this.pos;
if (
maxTokenLength > 1 && this.pos < this.end &&
maxTokenLength > 1 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
if (
maxTokenLength > 2 && this.pos < this.end &&
maxTokenLength > 2 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -588,7 +592,7 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.PERCENT: {
++this.pos;
if (
maxTokenLength > 1 && this.pos < this.end &&
maxTokenLength > 1 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -598,7 +602,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.AMPERSAND: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.AMPERSAND) {
++this.pos;
@ -621,7 +625,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.ASTERISK: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.EQUALS) {
++this.pos;
@ -630,7 +634,7 @@ export class Tokenizer extends DiagnosticEmitter {
if (chr == CharCode.ASTERISK) {
++this.pos;
if (
maxTokenLength > 2 && this.pos < this.end &&
maxTokenLength > 2 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -643,7 +647,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.PLUS: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.PLUS) {
++this.pos;
@ -662,7 +666,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.MINUS: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.MINUS) {
++this.pos;
@ -677,14 +681,14 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.DOT: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (isDecimalDigit(chr)) {
--this.pos;
return Token.FLOATLITERAL; // expects a call to readFloat
}
if (
maxTokenLength > 2 && this.pos + 1 < this.end &&
maxTokenLength > 2 && this.pos + 1 < end &&
chr == CharCode.DOT &&
text.charCodeAt(this.pos + 1) == CharCode.DOT
) {
@ -697,18 +701,18 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.SLASH: {
let commentStartPos = this.pos;
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.SLASH) { // single-line
let commentKind = CommentKind.LINE;
if (
this.pos + 1 < this.end &&
this.pos + 1 < end &&
text.charCodeAt(this.pos + 1) == CharCode.SLASH
) {
++this.pos;
commentKind = CommentKind.TRIPLE;
}
while (++this.pos < this.end) {
while (++this.pos < end) {
if (text.charCodeAt(this.pos) == CharCode.LINEFEED) {
++this.pos;
break;
@ -725,11 +729,11 @@ export class Tokenizer extends DiagnosticEmitter {
}
if (chr == CharCode.ASTERISK) { // multi-line
let closed = false;
while (++this.pos < this.end) {
while (++this.pos < end) {
c = text.charCodeAt(this.pos);
if (
c == CharCode.ASTERISK &&
this.pos + 1 < this.end &&
this.pos + 1 < end &&
text.charCodeAt(this.pos + 1) == CharCode.SLASH
) {
this.pos += 2;
@ -782,13 +786,13 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.LESSTHAN: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.LESSTHAN) {
++this.pos;
if (
maxTokenLength > 2 &&
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -805,13 +809,13 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.EQUALS: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.EQUALS) {
++this.pos;
if (
maxTokenLength > 2 &&
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -828,16 +832,16 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.GREATERTHAN: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.GREATERTHAN) {
++this.pos;
if (maxTokenLength > 2 && this.pos < this.end) {
if (maxTokenLength > 2 && this.pos < end) {
chr = text.charCodeAt(this.pos);
if (chr == CharCode.GREATERTHAN) {
++this.pos;
if (
maxTokenLength > 3 && this.pos < this.end &&
maxTokenLength > 3 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -874,7 +878,7 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.CARET: {
++this.pos;
if (
maxTokenLength > 1 && this.pos < this.end &&
maxTokenLength > 1 && this.pos < end &&
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
@ -888,7 +892,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.BAR: {
++this.pos;
if (maxTokenLength > 1 && this.pos < this.end) {
if (maxTokenLength > 1 && this.pos < end) {
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.BAR) {
++this.pos;
@ -918,7 +922,7 @@ export class Tokenizer extends DiagnosticEmitter {
if (isKeywordCharacter(c)) {
let posBefore = this.pos;
while (
++this.pos < this.end &&
++this.pos < end &&
isIdentifierPart(c = text.charCodeAt(this.pos))
) {
if (!isKeywordCharacter(c)) {
@ -1050,8 +1054,9 @@ export class Tokenizer extends DiagnosticEmitter {
readIdentifier(): string {
var text = this.source.text;
var start = this.pos;
var end = this.end;
while (
++this.pos < this.end &&
++this.pos < end &&
isIdentifierPart(text.charCodeAt(this.pos))
);
return text.substring(start, this.pos);
@ -1061,13 +1066,14 @@ export class Tokenizer extends DiagnosticEmitter {
var text = this.source.text;
var quote = text.charCodeAt(this.pos++);
var start = this.pos;
var end = this.end;
var result = "";
while (true) {
if (this.pos >= this.end) {
if (this.pos >= end) {
result += text.substring(start, this.pos);
this.error(
DiagnosticCode.Unterminated_string_literal,
this.range(start - 1, this.end)
this.range(start - 1, end)
);
break;
}
@ -1096,10 +1102,11 @@ export class Tokenizer extends DiagnosticEmitter {
}
readEscapeSequence(): string {
if (++this.pos >= this.end) {
var end = this.end;
if (++this.pos >= end) {
this.error(
DiagnosticCode.Unexpected_end_of_text,
this.range(this.end)
this.range(end)
);
return "";
}
@ -1118,7 +1125,7 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.DOUBLEQUOTE: return "\"";
case CharCode.u: {
if (
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) == CharCode.OPENBRACE
) {
++this.pos;
@ -1128,7 +1135,7 @@ export class Tokenizer extends DiagnosticEmitter {
}
case CharCode.CARRIAGERETURN: {
if (
this.pos < this.end &&
this.pos < end &&
text.charCodeAt(this.pos) == CharCode.LINEFEED
) {
++this.pos;
@ -1145,12 +1152,13 @@ export class Tokenizer extends DiagnosticEmitter {
readRegexpPattern(): string {
var text = this.source.text;
var start = this.pos;
var end = this.end;
var escaped = false;
while (true) {
if (this.pos >= this.end) {
if (this.pos >= end) {
this.error(
DiagnosticCode.Unterminated_regular_expression_literal,
this.range(start, this.end)
this.range(start, end)
);
break;
}
@ -1177,8 +1185,9 @@ export class Tokenizer extends DiagnosticEmitter {
readRegexpFlags(): string {
var text = this.source.text;
var start = this.pos;
var end = this.end;
var flags = 0;
while (this.pos < this.end) {
while (this.pos < end) {
let c: i32 = text.charCodeAt(this.pos);
if (!isIdentifierPart(c)) break;
++this.pos;
@ -1213,8 +1222,9 @@ export class Tokenizer extends DiagnosticEmitter {
}
testInteger(): bool {
var end = this.end;
var text = this.source.text;
if (this.pos + 1 < this.end && text.charCodeAt(this.pos) == CharCode._0) {
if (this.pos + 1 < end && text.charCodeAt(this.pos) == CharCode._0) {
switch (text.charCodeAt(this.pos + 2)) {
case CharCode.x:
case CharCode.X:
@ -1225,11 +1235,9 @@ export class Tokenizer extends DiagnosticEmitter {
}
}
var pos = this.pos;
while (pos < this.end) {
while (pos < end) {
let c = text.charCodeAt(pos);
if (c == CharCode.DOT || c == CharCode.e || c == CharCode.E) {
return false;
}
if (c == CharCode.DOT || c == CharCode.e || c == CharCode.E) return false;
if ((c < CharCode._0 || c > CharCode._9) && c != CharCode._) break;
// does not validate separator placement (this is done in readXYInteger)
pos++;
@ -1277,7 +1285,8 @@ export class Tokenizer extends DiagnosticEmitter {
var value = i64_new(0);
var i64_4 = i64_new(4);
var sepEnd = start;
while (this.pos < this.end) {
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
if (c >= CharCode._0 && c <= CharCode._9) {
@ -1330,10 +1339,11 @@ export class Tokenizer extends DiagnosticEmitter {
readDecimalInteger(): I64 {
var text = this.source.text;
var start = this.pos;
var end = this.end;
var value = i64_new(0);
var i64_10 = i64_new(10);
var sepEnd = start;
while (this.pos < this.end) {
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
if (c >= CharCode._0 && c <= CharCode._9) {
@ -1377,7 +1387,8 @@ export class Tokenizer extends DiagnosticEmitter {
var value = i64_new(0);
var i64_3 = i64_new(3);
var sepEnd = start;
while (this.pos < this.end) {
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
if (c >= CharCode._0 && c <= CharCode._7) {
@ -1421,7 +1432,8 @@ export class Tokenizer extends DiagnosticEmitter {
var value = i64_new(0);
var i64_1 = i64_new(1);
var sepEnd = start;
while (this.pos < this.end) {
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
if (c == CharCode._0) {
@ -1479,27 +1491,28 @@ export class Tokenizer extends DiagnosticEmitter {
readDecimalFloat(): f64 {
// TODO: numeric separators (parseFloat can't handle these)
var start = this.pos;
var end = this.end;
var text = this.source.text;
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
}
if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.DOT) {
if (this.pos < end && text.charCodeAt(this.pos) == CharCode.DOT) {
++this.pos;
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
}
}
if (this.pos < this.end) {
if (this.pos < end) {
let c = text.charCodeAt(this.pos);
if (c == CharCode.e || c == CharCode.E) {
if (
++this.pos < this.end &&
++this.pos < end &&
(c = text.charCodeAt(this.pos)) == CharCode.MINUS || c == CharCode.PLUS &&
isDecimalDigit(text.charCodeAt(this.pos + 1))
) {
++this.pos;
}
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
}
}
@ -1514,8 +1527,9 @@ export class Tokenizer extends DiagnosticEmitter {
readUnicodeEscape(): string {
var remain = 4;
var value = 0;
var end = this.end;
var text = this.source.text;
while (this.pos < this.end) {
while (this.pos < end) {
let c = text.charCodeAt(this.pos++);
if (c >= CharCode._0 && c <= CharCode._9) {
value = (value << 4) + c - CharCode._0;
@ -1557,11 +1571,12 @@ export class Tokenizer extends DiagnosticEmitter {
invalid = true;
}
var end = this.end;
var text = this.source.text;
if (this.pos >= this.end) {
if (this.pos >= end) {
this.error(
DiagnosticCode.Unexpected_end_of_text,
this.range(start, this.end)
this.range(start, end)
);
invalid = true;
} else if (text.charCodeAt(this.pos) == CharCode.CLOSEBRACE) {

View File

@ -2,6 +2,7 @@
"extends": "../std/portable.json",
"compilerOptions": {
"outDir": "../out",
"allowJs": false,
"sourceMap": true
},
"include": [

View File

@ -145,13 +145,21 @@ export class Type {
}
}
/** Gets this type's logarithmic alignment in memory. */
get alignLog2(): i32 {
return 31 - clz<i32>(this.byteSize);
}
/** Tests if this is a managed type that needs GC hooks. */
isManaged(program: Program): bool {
if (program.hasGC) {
let classReference = this.classReference;
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}
return false;
get isManaged(): bool {
var classReference = this.classReference;
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}
/** Tests if this is a class type explicitly annotated as unmanaged. */
get isUnmanaged(): bool {
var classReference = this.classReference;
return classReference !== null && classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}
/** Computes the sign-extending shift in the target type. */
@ -324,14 +332,14 @@ export class Type {
toNativeZero(module: Module): ExpressionRef {
switch (this.kind) {
case TypeKind.VOID: assert(false);
default: return module.createI32(0);
default: return module.i32(0);
case TypeKind.ISIZE:
case TypeKind.USIZE: if (this.size != 64) return module.createI32(0);
case TypeKind.USIZE: if (this.size != 64) return module.i32(0);
case TypeKind.I64:
case TypeKind.U64: return module.createI64(0);
case TypeKind.F32: return module.createF32(0);
case TypeKind.F64: return module.createF64(0);
case TypeKind.V128: return module.createV128(v128_zero);
case TypeKind.U64: return module.i64(0);
case TypeKind.F32: return module.f32(0);
case TypeKind.F64: return module.f64(0);
case TypeKind.V128: return module.v128(v128_zero);
}
}
@ -340,13 +348,13 @@ export class Type {
switch (this.kind) {
case TypeKind.V128:
case TypeKind.VOID: assert(false);
default: return module.createI32(1);
default: return module.i32(1);
case TypeKind.ISIZE:
case TypeKind.USIZE: if (this.size != 64) return module.createI32(1);
case TypeKind.USIZE: if (this.size != 64) return module.i32(1);
case TypeKind.I64:
case TypeKind.U64: return module.createI64(1);
case TypeKind.F32: return module.createF32(1);
case TypeKind.F64: return module.createF64(1);
case TypeKind.U64: return module.i64(1);
case TypeKind.F32: return module.f32(1);
case TypeKind.F64: return module.f64(1);
}
}
@ -355,13 +363,13 @@ export class Type {
switch (this.kind) {
case TypeKind.V128:
case TypeKind.VOID: assert(false);
default: return module.createI32(-1);
default: return module.i32(-1);
case TypeKind.ISIZE:
case TypeKind.USIZE: if (this.size != 64) return module.createI32(-1);
case TypeKind.USIZE: if (this.size != 64) return module.i32(-1);
case TypeKind.I64:
case TypeKind.U64: return module.createI64(-1, -1);
case TypeKind.F32: return module.createF32(-1);
case TypeKind.F64: return module.createF64(-1);
case TypeKind.U64: return module.i64(-1, -1);
case TypeKind.F32: return module.f32(-1);
case TypeKind.F64: return module.f64(-1);
}
}