mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-23 11:41:45 +00:00
Implement reference counting (#592)
This commit is contained in:
42
src/ast.ts
42
src/ast.ts
@ -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;
|
||||
|
1588
src/builtins.ts
1588
src/builtins.ts
File diff suppressed because it is too large
Load Diff
@ -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";
|
||||
|
12438
src/compiler.ts
12438
src/compiler.ts
File diff suppressed because it is too large
Load Diff
@ -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(")");
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.";
|
||||
|
@ -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,
|
||||
|
@ -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("");
|
||||
|
@ -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;
|
||||
|
627
src/flow.ts
627
src/flow.ts
@ -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);
|
||||
}
|
||||
}
|
||||
|
48
src/glue/binaryen.d.ts
vendored
48
src/glue/binaryen.d.ts
vendored
@ -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;
|
||||
|
72
src/index.ts
72
src/index.ts
@ -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";
|
||||
|
||||
|
351
src/module.ts
351
src/module.ts
@ -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;
|
||||
}
|
||||
|
106
src/parser.ts
106
src/parser.ts
@ -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;
|
||||
}
|
||||
|
||||
|
769
src/program.ts
769
src/program.ts
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
|
133
src/tokenizer.ts
133
src/tokenizer.ts
@ -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) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
"extends": "../std/portable.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out",
|
||||
"allowJs": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
|
52
src/types.ts
52
src/types.ts
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user