mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-16 08:21:44 +00:00
Rename memory instructions; Rework constant handling
This commit is contained in:
@ -121,8 +121,8 @@ export enum Op {
|
||||
i64_store8 = 0x3c,
|
||||
i64_store16 = 0x3d,
|
||||
i64_store32 = 0x3e,
|
||||
current_memory = 0x3f,
|
||||
grow_memory = 0x40,
|
||||
memory_size = 0x3f,
|
||||
memory_grow = 0x40,
|
||||
i32_const = 0x41,
|
||||
i64_const = 0x42,
|
||||
f32_const = 0x43,
|
||||
|
189
src/builtins.ts
189
src/builtins.ts
@ -48,13 +48,19 @@ import {
|
||||
Class,
|
||||
Field,
|
||||
OperatorKind,
|
||||
FlowFlags
|
||||
FlowFlags,
|
||||
Global,
|
||||
DecoratorFlags
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
ReportMode
|
||||
} from "./resolver";
|
||||
|
||||
import {
|
||||
CommonFlags
|
||||
} from "./common";
|
||||
|
||||
/** Compiles a call to a built-in function. */
|
||||
export function compileCall(
|
||||
compiler: Compiler,
|
||||
@ -134,24 +140,17 @@ export function compileCall(
|
||||
}
|
||||
case "isDefined": { // isDefined(expression) -> bool
|
||||
compiler.currentType = Type.bool;
|
||||
if (operands.length != 1) {
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
reportNode.range, prototype.internalName
|
||||
);
|
||||
}
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
reportNode.range, prototype.internalName
|
||||
);
|
||||
}
|
||||
if (operands.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let element = compiler.resolver.resolveExpression(operands[0], compiler.currentFunction, ReportMode.SWALLOW);
|
||||
@ -159,24 +158,17 @@ export function compileCall(
|
||||
}
|
||||
case "isConstant": { // isConstant(expression) -> bool
|
||||
compiler.currentType = Type.bool;
|
||||
if (operands.length != 1) {
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
reportNode.range, prototype.internalName
|
||||
);
|
||||
}
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
reportNode.range, prototype.internalName
|
||||
);
|
||||
}
|
||||
if (operands.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let expr = compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
|
||||
@ -1879,7 +1871,7 @@ export function compileCall(
|
||||
|
||||
// host operations
|
||||
|
||||
case "current_memory": { // current_memory() -> i32
|
||||
case "__memory_size": { // __memory_size() -> i32
|
||||
compiler.currentType = Type.i32;
|
||||
if (operands.length != 0) {
|
||||
compiler.error(
|
||||
@ -1895,7 +1887,7 @@ export function compileCall(
|
||||
}
|
||||
return module.createHost(HostOp.CurrentMemory);
|
||||
}
|
||||
case "grow_memory": { // grow_memory(pages: i32) -> i32
|
||||
case "__memory_grow": { // __memory_grow(pages: i32) -> i32
|
||||
compiler.currentType = Type.i32;
|
||||
if (operands.length != 1) {
|
||||
compiler.error(
|
||||
@ -1915,7 +1907,7 @@ export function compileCall(
|
||||
return module.createHost(HostOp.GrowMemory, null, [ arg0 ]);
|
||||
}
|
||||
// see: https://github.com/WebAssembly/bulk-memory-operations
|
||||
case "move_memory": { // move_memory(dest: usize, src: usize: n: usize) -> void
|
||||
case "__memory_copy": { // __memory_copy(dest: usize, src: usize: n: usize) -> void
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
@ -1952,7 +1944,7 @@ export function compileCall(
|
||||
throw new Error("not implemented");
|
||||
// return module.createHost(HostOp.MoveMemory, null, [ arg0, arg1, arg2 ]);
|
||||
}
|
||||
case "set_memory": { // set_memory(dest: usize, value: u8, n: usize) -> void
|
||||
case "__memory_fill": { // __memory_fill(dest: usize, value: u8, n: usize) -> void
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
@ -2318,6 +2310,30 @@ export function compileCall(
|
||||
return module.createCallIndirect(arg0, operandExprs, typeName);
|
||||
}
|
||||
|
||||
// user-defined diagnostic macros
|
||||
|
||||
case "ERROR": {
|
||||
compiler.error(
|
||||
DiagnosticCode.User_defined_0,
|
||||
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
case "WARNING": {
|
||||
compiler.warning(
|
||||
DiagnosticCode.User_defined_0,
|
||||
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
|
||||
);
|
||||
return module.createNop();
|
||||
}
|
||||
case "INFO": {
|
||||
compiler.info(
|
||||
DiagnosticCode.User_defined_0,
|
||||
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
|
||||
);
|
||||
return module.createNop();
|
||||
}
|
||||
|
||||
// conversions
|
||||
|
||||
case "i8": {
|
||||
@ -2610,6 +2626,84 @@ export function compileCall(
|
||||
WrapMode.NONE
|
||||
);
|
||||
}
|
||||
|
||||
// gc
|
||||
|
||||
case "__gc_iterate_roots": {
|
||||
// TOOD: make it so that this can only be called from a library file?
|
||||
if (typeArguments) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_generic,
|
||||
reportNode.range, prototype.internalName
|
||||
);
|
||||
}
|
||||
if (operands.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
compiler.currentType = Type.void;
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let expr = compiler.compileExpressionRetainType(operands[0], Type.u32, WrapMode.NONE);
|
||||
let type = compiler.currentType;
|
||||
let signatureReference = type.signatureReference;
|
||||
compiler.currentType = Type.void;
|
||||
if (
|
||||
!type.is(TypeFlags.REFERENCE) ||
|
||||
!signatureReference ||
|
||||
signatureReference.parameterTypes.length != 1 ||
|
||||
signatureReference.parameterTypes[0] != compiler.options.usizeType
|
||||
) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
|
||||
reportNode.range, type.toString(), "(ref: usize) => void"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let exprs = new Array<ExpressionRef>();
|
||||
for (let element of compiler.program.elementsLookup.values()) {
|
||||
if (element.kind != ElementKind.GLOBAL) continue;
|
||||
let global = <Global>element;
|
||||
let classReference = global.type.classReference;
|
||||
if (
|
||||
global.is(CommonFlags.COMPILED) &&
|
||||
classReference !== null &&
|
||||
!classReference.hasDecorator(DecoratorFlags.UNMANAGED)
|
||||
) {
|
||||
if (global.is(CommonFlags.INLINED)) {
|
||||
let value = global.constantIntegerValue;
|
||||
exprs.push(
|
||||
module.createCallIndirect(
|
||||
expr,
|
||||
[
|
||||
compiler.options.isWasm64
|
||||
? module.createI64(i64_low(value), i64_high(value))
|
||||
: module.createI32(i64_low(value))
|
||||
],
|
||||
signatureReference.toSignatureString()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
exprs.push(
|
||||
module.createCallIndirect(
|
||||
expr,
|
||||
[
|
||||
module.createGetGlobal(
|
||||
global.internalName,
|
||||
compiler.options.nativeSizeType
|
||||
)
|
||||
],
|
||||
signatureReference.toSignatureString()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return exprs.length
|
||||
? module.createBlock(null, exprs)
|
||||
: module.createNop();
|
||||
}
|
||||
}
|
||||
var expr = deferASMCall(compiler, prototype, operands, contextualType, reportNode);
|
||||
if (expr) {
|
||||
@ -2816,8 +2910,6 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
|
||||
return value;
|
||||
}
|
||||
|
||||
const allocateInternalName = "allocate_memory";
|
||||
|
||||
/** Compiles a memory allocation for an instance of the specified class. */
|
||||
export function compileAllocate(
|
||||
compiler: Compiler,
|
||||
@ -2828,29 +2920,15 @@ export function compileAllocate(
|
||||
assert(classInstance.program == program);
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
|
||||
var allocatePrototype = program.elementsLookup.get(allocateInternalName);
|
||||
if (!allocatePrototype) {
|
||||
var allocateInstance = program.memoryAllocateInstance;
|
||||
if (!allocateInstance) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, allocateInternalName
|
||||
);
|
||||
program.info(
|
||||
DiagnosticCode.An_allocator_must_be_declared_to_allocate_memory_Try_importing_allocator_arena_or_allocator_tlsf,
|
||||
reportNode.range
|
||||
reportNode.range, "memory.allocate"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (allocatePrototype.kind != ElementKind.FUNCTION_PROTOTYPE) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
|
||||
reportNode.range, allocatePrototype.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
|
||||
var allocateInstance = compiler.resolver.resolveFunction(<FunctionPrototype>allocatePrototype, null);
|
||||
if (!(allocateInstance && compiler.compileFunction(allocateInstance))) return module.createUnreachable();
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
@ -2863,8 +2941,6 @@ export function compileAllocate(
|
||||
);
|
||||
}
|
||||
|
||||
const abortInternalName = "abort";
|
||||
|
||||
/** Compiles an abort wired to the conditionally imported 'abort' function. */
|
||||
export function compileAbort(
|
||||
compiler: Compiler,
|
||||
@ -2874,13 +2950,10 @@ export function compileAbort(
|
||||
var program = compiler.program;
|
||||
var module = compiler.module;
|
||||
|
||||
var stringType = program.typesLookup.get("string"); // might be intended
|
||||
var stringType = program.typesLookup.get("string");
|
||||
if (!stringType) return module.createUnreachable();
|
||||
|
||||
var abortPrototype = program.elementsLookup.get(abortInternalName); // might be intended
|
||||
if (!abortPrototype || abortPrototype.kind != ElementKind.FUNCTION_PROTOTYPE) return module.createUnreachable();
|
||||
|
||||
var abortInstance = compiler.resolver.resolveFunction(<FunctionPrototype>abortPrototype, null);
|
||||
var abortInstance = program.abortInstance;
|
||||
if (!(abortInstance && compiler.compileFunction(abortInstance))) return module.createUnreachable();
|
||||
|
||||
var messageArg = message != null
|
||||
|
577
src/compiler.ts
577
src/compiler.ts
@ -32,7 +32,6 @@ import {
|
||||
getConstValueI64High,
|
||||
getConstValueF32,
|
||||
getConstValueF64,
|
||||
getFunctionBody,
|
||||
getGetLocalIndex,
|
||||
getBlockChildCount,
|
||||
getBlockChild,
|
||||
@ -404,9 +403,172 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (!functionTableExported) module.addTableExport("0", "table");
|
||||
}
|
||||
|
||||
// set up module exports
|
||||
for (let [name, moduleExport] of program.moduleLevelExports) {
|
||||
this.makeModuleExport(name, moduleExport.element);
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/** Applies the respective module export(s) for the specified element. */
|
||||
private makeModuleExport(name: string, element: Element, prefix: string = ""): void {
|
||||
|
||||
// traverse members
|
||||
var members = element.members;
|
||||
if (members) {
|
||||
let subPrefix = prefix + name + (element.kind == ElementKind.CLASS
|
||||
? INSTANCE_DELIMITER
|
||||
: STATIC_DELIMITER
|
||||
);
|
||||
if (element.kind == ElementKind.NAMESPACE) {
|
||||
for (let member of members.values()) {
|
||||
if (!member.is(CommonFlags.EXPORT)) continue;
|
||||
this.makeModuleExport(member.simpleName, member, subPrefix);
|
||||
}
|
||||
} else {
|
||||
for (let member of members.values()) {
|
||||
if (member.is(CommonFlags.PRIVATE)) continue;
|
||||
this.makeModuleExport(member.simpleName, member, subPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (element.kind) {
|
||||
|
||||
// export global
|
||||
case ElementKind.GLOBAL: {
|
||||
let isConst = element.is(CommonFlags.CONST) || element.is(CommonFlags.STATIC | CommonFlags.READONLY);
|
||||
if (!isConst && !this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
let declaration = (<Global>element).declaration;
|
||||
if (declaration) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_export_a_mutable_global,
|
||||
declaration.name.range
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.module.addGlobalExport(element.internalName, prefix + name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.ENUMVALUE: {
|
||||
if (!assert(element.parent).is(CommonFlags.CONST) && !this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
let declaration = (<EnumValue>element).declaration;
|
||||
if (declaration) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_export_a_mutable_global,
|
||||
declaration.name.range
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.module.addGlobalExport(element.internalName, prefix + name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// export function
|
||||
case ElementKind.FUNCTION: {
|
||||
let instance = <Function>element;
|
||||
let signature = instance.signature;
|
||||
if (signature.requiredParameters < signature.parameterTypes.length) {
|
||||
// utilize trampoline to fill in omitted arguments
|
||||
instance = this.ensureTrampoline(instance);
|
||||
this.ensureArgcSet();
|
||||
}
|
||||
if (instance.is(CommonFlags.COMPILED)) this.module.addFunctionExport(instance.internalName, prefix + name);
|
||||
break;
|
||||
}
|
||||
|
||||
// export getter and setter
|
||||
case ElementKind.PROPERTY: {
|
||||
let getter = assert((<Property>element).getterPrototype);
|
||||
this.makeModuleExport(GETTER_PREFIX + name, getter, prefix);
|
||||
let setter = (<Property>element).setterPrototype;
|
||||
if (setter) this.makeModuleExport(SETTER_PREFIX + name, setter, prefix);
|
||||
break;
|
||||
}
|
||||
|
||||
// export a getter and a setter
|
||||
case ElementKind.FIELD: {
|
||||
let module = this.module;
|
||||
let type = (<Field>element).type;
|
||||
let nativeType = type.toNativeType();
|
||||
let offset = (<Field>element).memoryOffset;
|
||||
let usizeType = this.options.usizeType;
|
||||
let nativeSizeType = this.options.nativeSizeType;
|
||||
|
||||
// make a getter
|
||||
let getterName = prefix + GETTER_PREFIX + name;
|
||||
module.addFunction(
|
||||
getterName,
|
||||
this.ensureFunctionType(null, type, usizeType),
|
||||
null,
|
||||
module.createLoad(
|
||||
type.byteSize,
|
||||
type.is(TypeFlags.SIGNED),
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeType,
|
||||
offset
|
||||
)
|
||||
);
|
||||
module.addFunctionExport(getterName, getterName);
|
||||
|
||||
// make a setter
|
||||
if (!element.is(CommonFlags.READONLY)) {
|
||||
let setterName = prefix + SETTER_PREFIX + name;
|
||||
module.addFunction(
|
||||
setterName,
|
||||
this.ensureFunctionType([ type ], Type.void, usizeType),
|
||||
null,
|
||||
module.createStore(
|
||||
type.byteSize,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
module.createGetLocal(1, nativeType),
|
||||
nativeType,
|
||||
offset
|
||||
)
|
||||
);
|
||||
module.addFunctionExport(setterName, setterName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// skip prototype and export instances
|
||||
case ElementKind.FUNCTION_PROTOTYPE: {
|
||||
for (let instance of (<FunctionPrototype>element).instances.values()) {
|
||||
let instanceName = name;
|
||||
if (instance.is(CommonFlags.GENERIC)) {
|
||||
let fullName = instance.internalName;
|
||||
instanceName += fullName.substring(fullName.lastIndexOf("<"));
|
||||
}
|
||||
this.makeModuleExport(instanceName, instance, prefix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS_PROTOTYPE: {
|
||||
for (let instance of (<ClassPrototype>element).instances.values()) {
|
||||
let instanceName = name;
|
||||
if (instance.is(CommonFlags.GENERIC)) {
|
||||
let fullName = instance.internalName;
|
||||
instanceName += fullName.substring(fullName.lastIndexOf("<"));
|
||||
}
|
||||
let ctor = instance.constructorInstance;
|
||||
if (ctor) this.makeModuleExport(instanceName + INSTANCE_DELIMITER + ctor.simpleName, ctor, prefix);
|
||||
this.makeModuleExport(instanceName, instance, prefix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// all possible members already handled above
|
||||
case ElementKind.ENUM:
|
||||
case ElementKind.CLASS:
|
||||
case ElementKind.NAMESPACE: break;
|
||||
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// sources
|
||||
|
||||
/** Compiles a source by looking it up by path first. */
|
||||
@ -568,13 +730,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (global.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) return true;
|
||||
|
||||
var nativeType = global.type.toNativeType();
|
||||
var isConstant = global.isAny(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
|
||||
var isDeclaredConstant = global.is(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
|
||||
|
||||
// handle imports
|
||||
if (global.is(CommonFlags.AMBIENT)) {
|
||||
|
||||
// constant global
|
||||
if (isConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
if (isDeclaredConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
global.set(CommonFlags.MODULE_IMPORT);
|
||||
if (declaration) {
|
||||
mangleImportName(global, declaration, global.parent);
|
||||
@ -601,49 +763,77 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the MVP does not yet support initializer expressions other than constant values (and
|
||||
// the MVP does not yet support initializer expressions other than constant values (and constant
|
||||
// get_globals), hence such initializations must be performed in the start function for now.
|
||||
var initializeInStart = false;
|
||||
|
||||
// inlined constant can be compiled as-is
|
||||
if (global.is(CommonFlags.INLINED)) {
|
||||
initExpr = this.compileInlineConstant(global, global.type, true);
|
||||
// evaluate initializer if present
|
||||
if (declaration !== null && declaration.initializer !== null) {
|
||||
if (!initExpr) {
|
||||
initExpr = this.compileExpression(
|
||||
declaration.initializer,
|
||||
global.type,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.WRAP
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// evaluate initializer if present
|
||||
if (declaration && declaration.initializer) {
|
||||
if (!initExpr) {
|
||||
initExpr = this.compileExpression(
|
||||
declaration.initializer,
|
||||
global.type,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.WRAP
|
||||
);
|
||||
}
|
||||
|
||||
// check if the initializer is constant
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
|
||||
// if a constant global, check if the initializer becomes constant after precompute
|
||||
if (isConstant) {
|
||||
initExpr = this.precomputeExpressionRef(initExpr);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
declaration.range
|
||||
);
|
||||
initializeInStart = true;
|
||||
}
|
||||
} else {
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
if (isDeclaredConstant) {
|
||||
initExpr = module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
declaration.range
|
||||
);
|
||||
initializeInStart = true;
|
||||
}
|
||||
} else {
|
||||
initializeInStart = true;
|
||||
}
|
||||
|
||||
// initialize to zero if there's no initializer
|
||||
} else {
|
||||
initExpr = global.type.toNativeZero(module);
|
||||
}
|
||||
|
||||
// explicitly inline if annotated
|
||||
if (global.hasDecorator(DecoratorFlags.INLINE)) {
|
||||
if (!initializeInStart) { // reported above
|
||||
assert(getExpressionId(initExpr) == ExpressionId.Const);
|
||||
let exprType = getExpressionType(initExpr);
|
||||
switch (exprType) {
|
||||
case NativeType.I32: {
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(getConstValueI32(initExpr), 0);
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(
|
||||
getConstValueI64Low(initExpr),
|
||||
getConstValueI64High(initExpr)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = getConstValueF32(initExpr);
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = getConstValueF64(initExpr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
global.set(CommonFlags.INLINED); // inline the value from now on
|
||||
}
|
||||
}
|
||||
|
||||
// initialize to zero if there's no initializer
|
||||
} else {
|
||||
initExpr = global.type.toNativeZero(module);
|
||||
}
|
||||
|
||||
var internalName = global.internalName;
|
||||
@ -652,50 +842,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.addGlobal(internalName, nativeType, true, global.type.toNativeZero(module));
|
||||
this.startFunctionBody.push(module.createSetGlobal(internalName, initExpr));
|
||||
|
||||
} else { // compile as-is
|
||||
|
||||
if (isConstant) {
|
||||
let exprType = getExpressionType(initExpr);
|
||||
switch (exprType) {
|
||||
case NativeType.I32: {
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(getConstValueI32(initExpr), 0);
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(
|
||||
getConstValueI64Low(initExpr),
|
||||
getConstValueI64High(initExpr)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = getConstValueF32(initExpr);
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = getConstValueF64(initExpr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
global.set(CommonFlags.INLINED); // inline the value from now on
|
||||
if (global.is(CommonFlags.MODULE_EXPORT)) {
|
||||
module.addGlobal(internalName, nativeType, false, initExpr);
|
||||
module.addGlobalExport(internalName, mangleExportName(global));
|
||||
} else if (declaration && declaration.isTopLevel) { // might become re-exported
|
||||
module.addGlobal(internalName, nativeType, false, initExpr);
|
||||
}
|
||||
|
||||
} else /* mutable */ {
|
||||
module.addGlobal(internalName, nativeType, !isConstant, initExpr);
|
||||
}
|
||||
} else { // compile normally
|
||||
module.addGlobal(internalName, nativeType, !isDeclaredConstant, initExpr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -716,6 +864,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var module = this.module;
|
||||
this.currentEnum = element;
|
||||
var previousValue: EnumValue | null = null;
|
||||
var previousValueIsMut = false;
|
||||
|
||||
if (element.members) {
|
||||
for (let member of element.members.values()) {
|
||||
@ -724,86 +873,61 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let val = <EnumValue>member;
|
||||
let valueDeclaration = val.declaration;
|
||||
val.set(CommonFlags.COMPILED);
|
||||
if (val.is(CommonFlags.INLINED)) {
|
||||
if (element.declaration.isTopLevelExport) {
|
||||
module.addGlobal(
|
||||
val.internalName,
|
||||
NativeType.I32,
|
||||
false, // constant
|
||||
module.createI32(val.constantValue)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let initExpr: ExpressionRef;
|
||||
if (valueDeclaration.value) {
|
||||
initExpr = this.compileExpression(
|
||||
<Expression>valueDeclaration.value,
|
||||
Type.i32,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
initExpr = this.precomputeExpressionRef(initExpr);
|
||||
let initExpr: ExpressionRef;
|
||||
if (valueDeclaration.value) {
|
||||
initExpr = this.compileExpression(
|
||||
<Expression>valueDeclaration.value,
|
||||
Type.i32,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
if (element.is(CommonFlags.CONST)) {
|
||||
initExpr = module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
if (element.is(CommonFlags.CONST)) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
valueDeclaration.range
|
||||
);
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode.In_const_enum_declarations_member_initializer_must_be_constant_expression,
|
||||
valueDeclaration.value.range
|
||||
);
|
||||
initInStart = true;
|
||||
}
|
||||
} else {
|
||||
initInStart = true;
|
||||
}
|
||||
} else if (previousValue == null) {
|
||||
initExpr = module.createI32(0);
|
||||
} else if (previousValue.is(CommonFlags.INLINED)) {
|
||||
initExpr = module.createI32(previousValue.constantValue + 1);
|
||||
} else {
|
||||
// in TypeScript this errors with TS1061, but actually we can do:
|
||||
initExpr = module.createBinary(BinaryOp.AddI32,
|
||||
module.createGetGlobal(previousValue.internalName, NativeType.I32),
|
||||
module.createI32(1)
|
||||
}
|
||||
} else if (previousValue == null) {
|
||||
initExpr = module.createI32(0);
|
||||
} else {
|
||||
if (previousValueIsMut) {
|
||||
this.error(
|
||||
DiagnosticCode.Enum_member_must_have_initializer,
|
||||
valueDeclaration.range
|
||||
);
|
||||
}
|
||||
initExpr = module.createBinary(BinaryOp.AddI32,
|
||||
module.createGetGlobal(previousValue.internalName, NativeType.I32),
|
||||
module.createI32(1)
|
||||
);
|
||||
initExpr = module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
if (element.is(CommonFlags.CONST)) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
this.error(
|
||||
DiagnosticCode.In_const_enum_declarations_member_initializer_must_be_constant_expression,
|
||||
valueDeclaration.range
|
||||
);
|
||||
}
|
||||
initInStart = true;
|
||||
}
|
||||
if (initInStart) {
|
||||
module.addGlobal(
|
||||
val.internalName,
|
||||
NativeType.I32,
|
||||
true, // mutable
|
||||
module.createI32(0)
|
||||
);
|
||||
this.startFunctionBody.push(module.createSetGlobal(val.internalName, initExpr));
|
||||
} else {
|
||||
module.addGlobal(val.internalName, NativeType.I32, false, initExpr);
|
||||
if (getExpressionType(initExpr) == NativeType.I32) {
|
||||
val.constantValue = getConstValueI32(initExpr);
|
||||
val.set(CommonFlags.INLINED);
|
||||
} else {
|
||||
assert(false);
|
||||
val.constantValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (initInStart) {
|
||||
module.addGlobal(val.internalName, NativeType.I32, true, module.createI32(0));
|
||||
this.startFunctionBody.push(module.createSetGlobal(val.internalName, initExpr));
|
||||
previousValueIsMut = true;
|
||||
} else {
|
||||
module.addGlobal(val.internalName, NativeType.I32, !element.is(CommonFlags.CONST), initExpr);
|
||||
previousValueIsMut = false;
|
||||
}
|
||||
previousValue = <EnumValue>val;
|
||||
|
||||
// export values if the enum is exported
|
||||
if (element.is(CommonFlags.MODULE_EXPORT)) {
|
||||
if (member.is(CommonFlags.INLINED) || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
module.addGlobalExport(member.internalName, mangleExportName(member));
|
||||
} else if (valueDeclaration) {
|
||||
this.warning(
|
||||
DiagnosticCode.Cannot_export_a_mutable_global,
|
||||
valueDeclaration.range
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.currentEnum = null;
|
||||
@ -1015,16 +1139,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
// check module-level export
|
||||
if (instance.is(CommonFlags.MODULE_EXPORT)) {
|
||||
if (signature.requiredParameters < signature.parameterTypes.length) {
|
||||
// export the trampoline if the function takes optional parameters
|
||||
instance = this.ensureTrampoline(instance);
|
||||
this.ensureArgcSet();
|
||||
}
|
||||
module.addFunctionExport(instance.internalName, mangleExportName(instance));
|
||||
}
|
||||
|
||||
instance.finalize(module, ref);
|
||||
return true;
|
||||
}
|
||||
@ -1140,18 +1254,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// exports
|
||||
|
||||
compileExportStatement(statement: ExportStatement): void {
|
||||
var module = this.module;
|
||||
var fileLevelExports = this.program.fileLevelExports;
|
||||
var members = statement.members;
|
||||
if (!members) return; // filespace
|
||||
for (let i = 0, k = members.length; i < k; ++i) {
|
||||
let member = members[i];
|
||||
let internalExportName = (
|
||||
statement.range.source.internalPath +
|
||||
PATH_DELIMITER +
|
||||
member.externalName.text
|
||||
let element = fileLevelExports.get(
|
||||
statement.range.source.internalPath + PATH_DELIMITER + member.externalName.text
|
||||
);
|
||||
let element = fileLevelExports.get(internalExportName);
|
||||
if (!element) continue; // reported in Program#initialize
|
||||
switch (element.kind) {
|
||||
case ElementKind.CLASS_PROTOTYPE: {
|
||||
@ -1169,36 +1279,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
!(<FunctionPrototype>element).is(CommonFlags.GENERIC) &&
|
||||
statement.range.source.isEntry
|
||||
) {
|
||||
let functionInstance = this.compileFunctionUsingTypeArguments(
|
||||
this.compileFunctionUsingTypeArguments(
|
||||
<FunctionPrototype>element,
|
||||
[],
|
||||
null, // no contextual type arguments
|
||||
null, // no outer scope
|
||||
(<FunctionPrototype>element).declaration.name
|
||||
);
|
||||
if (functionInstance) {
|
||||
let functionDeclaration = functionInstance.prototype.declaration;
|
||||
if (functionDeclaration && functionDeclaration.needsExplicitExport(member)) {
|
||||
module.addFunctionExport(functionInstance.internalName, member.externalName.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.GLOBAL: {
|
||||
if (this.compileGlobal(<Global>element) && statement.range.source.isEntry) {
|
||||
let globalDeclaration = (<Global>element).declaration;
|
||||
if (globalDeclaration && globalDeclaration.needsExplicitExport(member)) {
|
||||
if ((<Global>element).is(CommonFlags.INLINED)) {
|
||||
module.addGlobalExport(element.internalName, member.externalName.text);
|
||||
} else {
|
||||
this.warning(
|
||||
DiagnosticCode.Cannot_export_a_mutable_global,
|
||||
member.range
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.compileGlobal(<Global>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.NAMESPACE: {
|
||||
@ -1310,47 +1402,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
element.set(CommonFlags.COMPILED);
|
||||
if (!instance.is(CommonFlags.MODULE_EXPORT) || element.is(CommonFlags.PRIVATE)) break;
|
||||
let module = this.module;
|
||||
let name = (<Field>element).simpleName;
|
||||
let type = (<Field>element).type;
|
||||
let nativeType = type.toNativeType();
|
||||
let offset = (<Field>element).memoryOffset;
|
||||
let usizeType = this.options.usizeType;
|
||||
let nativeSizeType = this.options.nativeSizeType;
|
||||
|
||||
// export an implicit getter: get:fieldName(this: usize) -> fieldType
|
||||
let getterName = mangleExportName(element, GETTER_PREFIX + name);
|
||||
module.addFunction(
|
||||
getterName,
|
||||
this.ensureFunctionType(null, type, usizeType),
|
||||
null,
|
||||
module.createLoad(
|
||||
type.byteSize,
|
||||
type.is(TypeFlags.SIGNED),
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeType,
|
||||
offset
|
||||
)
|
||||
);
|
||||
module.addFunctionExport(getterName, getterName);
|
||||
|
||||
// export an implicit setter: set:fieldName(this: usize, value: fieldType) -> void
|
||||
if (element.is(CommonFlags.READONLY)) break;
|
||||
let setterName = mangleExportName(element, SETTER_PREFIX + name);
|
||||
module.addFunction(
|
||||
setterName,
|
||||
this.ensureFunctionType([ type ], Type.void, usizeType),
|
||||
null,
|
||||
module.createStore(
|
||||
type.byteSize,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
module.createGetLocal(1, nativeType),
|
||||
nativeType,
|
||||
offset
|
||||
)
|
||||
);
|
||||
module.addFunctionExport(setterName, setterName);
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: {
|
||||
@ -1676,7 +1727,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentType
|
||||
);
|
||||
// check if the condition is always true
|
||||
let condPre = this.precomputeExpressionRef(condExpr);
|
||||
let condPre = module.precomputeExpression(condExpr);
|
||||
if (getExpressionId(condPre) == ExpressionId.Const) {
|
||||
assert(getExpressionType(condPre) == NativeType.I32);
|
||||
if (getConstValueI32(condPre) != 0) alwaysTrue = true;
|
||||
@ -1766,7 +1817,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
|
||||
) {
|
||||
// Try to eliminate unnecesssary branches if the condition is constant
|
||||
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
|
||||
let condExprPrecomp = module.precomputeExpression(condExpr);
|
||||
if (
|
||||
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
|
||||
getExpressionType(condExprPrecomp) == NativeType.I32
|
||||
@ -2057,7 +2108,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let isInlined = false;
|
||||
if (declaration.is(CommonFlags.CONST)) {
|
||||
if (initExpr) {
|
||||
initExpr = this.precomputeExpressionRef(initExpr);
|
||||
initExpr = this.module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) == ExpressionId.Const) {
|
||||
let local = new Local(program, name, -1, type);
|
||||
switch (getExpressionType(initExpr)) {
|
||||
@ -2162,7 +2213,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
|
||||
) {
|
||||
// Try to eliminate unnecesssary loops if the condition is constant
|
||||
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
|
||||
let condExprPrecomp = module.precomputeExpression(condExpr);
|
||||
if (
|
||||
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
|
||||
getExpressionType(condExprPrecomp) == NativeType.I32
|
||||
@ -2432,22 +2483,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
conversionKind: ConversionKind,
|
||||
wrapMode: WrapMode
|
||||
): ExpressionRef {
|
||||
return this.precomputeExpressionRef(
|
||||
return this.module.precomputeExpression(
|
||||
this.compileExpression(expression, contextualType, conversionKind, wrapMode)
|
||||
);
|
||||
}
|
||||
|
||||
precomputeExpressionRef(expr: ExpressionRef): ExpressionRef {
|
||||
var module = this.module;
|
||||
var type = this.currentType;
|
||||
var nativeType = type.toNativeType();
|
||||
var funcRef = module.addTemporaryFunction(nativeType, null, expr);
|
||||
module.runPasses([ "precompute" ], funcRef);
|
||||
var ret = getFunctionBody(funcRef);
|
||||
module.removeTemporaryFunction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
convertExpression(
|
||||
expr: ExpressionRef,
|
||||
fromType: Type,
|
||||
@ -5497,8 +5537,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
names[i] = label;
|
||||
}
|
||||
var body = module.createBlock(names[0], [
|
||||
module.createBlock("oob", [
|
||||
module.createSwitch(names, "oob",
|
||||
module.createBlock("outOfRange", [
|
||||
module.createSwitch(names, "outOfRange",
|
||||
// condition is number of provided optional arguments, so subtract required arguments
|
||||
minArguments
|
||||
? module.createBinary(
|
||||
@ -5946,10 +5986,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
switch (target.kind) {
|
||||
case ElementKind.LOCAL: {
|
||||
let localType = (<Local>target).type;
|
||||
assert(localType != Type.void);
|
||||
if ((<Local>target).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Local>target, contextualType, retainConstantType);
|
||||
}
|
||||
let localType = (<Local>target).type;
|
||||
let localIndex = (<Local>target).index;
|
||||
assert(localIndex >= 0);
|
||||
this.currentType = localType;
|
||||
@ -6252,7 +6293,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
? this.compileExpression(<Expression>expressions[i], elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: elementType.toNativeZero(module);
|
||||
if (isStatic) {
|
||||
expr = this.precomputeExpressionRef(exprs[i]);
|
||||
expr = module.precomputeExpression(exprs[i]);
|
||||
if (getExpressionId(expr) == ExpressionId.Const) {
|
||||
assert(getExpressionType(expr) == nativeElementType);
|
||||
switch (nativeElementType) {
|
||||
@ -6601,6 +6642,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let parent = (<EnumValue>target).parent;
|
||||
assert(parent !== null && parent.kind == ElementKind.ENUM);
|
||||
if (!this.compileEnum(<Enum>parent)) {
|
||||
this.currentType = Type.i32;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
this.currentType = Type.i32;
|
||||
@ -6688,7 +6730,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
|
||||
) {
|
||||
// Try to eliminate unnecesssary branches if the condition is constant
|
||||
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
|
||||
let condExprPrecomp = this.module.precomputeExpression(condExpr);
|
||||
if (
|
||||
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
|
||||
getExpressionType(condExprPrecomp) == NativeType.I32
|
||||
@ -7536,47 +7578,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// helpers
|
||||
|
||||
function mangleExportName(element: Element, simpleName: string = element.simpleName): string {
|
||||
switch (element.kind) {
|
||||
case ElementKind.FUNCTION: {
|
||||
let parent = (<Function>element).parent || (<Function>element).prototype.parent;
|
||||
return parent
|
||||
? mangleExportName(parent)
|
||||
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
|
||||
+ simpleName
|
||||
: simpleName;
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
let parent = assert((<Field>element).parent);
|
||||
return mangleExportName(parent)
|
||||
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
|
||||
+ simpleName;
|
||||
}
|
||||
case ElementKind.ENUMVALUE: {
|
||||
let parent = assert((<EnumValue>element).parent);
|
||||
return mangleExportName(parent)
|
||||
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
|
||||
+ simpleName;
|
||||
}
|
||||
case ElementKind.CLASS: {
|
||||
let parent = (<Class>element).prototype.parent;
|
||||
return parent
|
||||
? mangleExportName(parent)
|
||||
+ STATIC_DELIMITER
|
||||
+ simpleName
|
||||
: simpleName;
|
||||
}
|
||||
default: {
|
||||
let parent = element.parent;
|
||||
return parent
|
||||
? mangleExportName(parent)
|
||||
+ STATIC_DELIMITER
|
||||
+ simpleName
|
||||
: simpleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mangleImportName(
|
||||
element: Element,
|
||||
declaration: DeclarationStatement,
|
||||
|
@ -834,11 +834,11 @@ export class Decompiler {
|
||||
case ExpressionId.Host: {
|
||||
switch (getHostOp(expr)) {
|
||||
case HostOp.CurrentMemory: {
|
||||
this.push("current_memory()");
|
||||
this.push("memory.size()");
|
||||
return;
|
||||
}
|
||||
case HostOp.GrowMemory: {
|
||||
this.push("grow_memory(");
|
||||
this.push("memory.grow(");
|
||||
this.decompileExpression(getHostOperand(expr, 0));
|
||||
this.push(")");
|
||||
return;
|
||||
|
@ -54,7 +54,10 @@ abstract class ExportsWalker {
|
||||
|
||||
/** Walks all exports and calls the respective handlers. */
|
||||
walk(): void {
|
||||
for (let element of this.program.moduleLevelExports.values()) this.visitElement(element);
|
||||
for (let moduleExport of this.program.moduleLevelExports.values()) {
|
||||
// FIXME: doesn't honor the actual externally visible name
|
||||
this.visitElement(moduleExport.element);
|
||||
}
|
||||
var todo = this.todo;
|
||||
for (let i = 0; i < todo.length; ) this.visitElement(todo[i]);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
export enum DiagnosticCode {
|
||||
Operation_not_supported = 100,
|
||||
Operation_is_unsafe = 101,
|
||||
User_defined_0 = 102,
|
||||
Conversion_from_type_0_to_1_requires_an_explicit_cast = 200,
|
||||
Conversion_from_type_0_to_1_will_require_an_explicit_cast_when_switching_between_32_64_bit = 201,
|
||||
Type_0_cannot_be_changed_to_type_1 = 202,
|
||||
@ -36,7 +37,6 @@ export enum DiagnosticCode {
|
||||
A_rest_parameter_must_be_last_in_a_parameter_list = 1014,
|
||||
Parameter_cannot_have_question_mark_and_initializer = 1015,
|
||||
A_required_parameter_cannot_follow_an_optional_parameter = 1016,
|
||||
Enum_member_must_have_initializer = 1061,
|
||||
Statements_are_not_allowed_in_ambient_contexts = 1036,
|
||||
Initializers_are_not_allowed_in_ambient_contexts = 1039,
|
||||
_0_modifier_cannot_be_used_here = 1042,
|
||||
@ -45,6 +45,7 @@ export enum DiagnosticCode {
|
||||
A_set_accessor_must_have_exactly_one_parameter = 1049,
|
||||
A_set_accessor_parameter_cannot_have_an_initializer = 1052,
|
||||
A_get_accessor_cannot_have_parameters = 1054,
|
||||
Enum_member_must_have_initializer = 1061,
|
||||
Type_parameters_cannot_appear_on_a_constructor_declaration = 1092,
|
||||
Type_annotation_cannot_appear_on_a_constructor_declaration = 1093,
|
||||
An_accessor_cannot_have_type_parameters = 1094,
|
||||
@ -105,6 +106,7 @@ export enum DiagnosticCode {
|
||||
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local = 2395,
|
||||
Type_0_has_no_property_1 = 2460,
|
||||
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,
|
||||
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,
|
||||
@ -126,6 +128,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
switch (code) {
|
||||
case 100: return "Operation not supported.";
|
||||
case 101: return "Operation is unsafe.";
|
||||
case 102: return "User-defined: {0}";
|
||||
case 200: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
|
||||
case 201: return "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.";
|
||||
case 202: return "Type '{0}' cannot be changed to type '{1}'.";
|
||||
@ -153,7 +156,6 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1014: return "A rest parameter must be last in a parameter list.";
|
||||
case 1015: return "Parameter cannot have question mark and initializer.";
|
||||
case 1016: return "A required parameter cannot follow an optional parameter.";
|
||||
case 1061: return "Enum member must have initializer.";
|
||||
case 1036: return "Statements are not allowed in ambient contexts.";
|
||||
case 1039: return "Initializers are not allowed in ambient contexts.";
|
||||
case 1042: return "'{0}' modifier cannot be used here.";
|
||||
@ -162,6 +164,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1049: return "A 'set' accessor must have exactly one parameter.";
|
||||
case 1052: return "A 'set' accessor parameter cannot have an initializer.";
|
||||
case 1054: return "A 'get' accessor cannot have parameters.";
|
||||
case 1061: return "Enum member must have initializer.";
|
||||
case 1092: return "Type parameters cannot appear on a constructor declaration.";
|
||||
case 1093: return "Type annotation cannot appear on a constructor declaration.";
|
||||
case 1094: return "An accessor cannot have type parameters.";
|
||||
@ -222,6 +225,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2395: return "Individual declarations in merged declaration '{0}' must be all exported or all local.";
|
||||
case 2460: return "Type '{0}' has no property '{1}'.";
|
||||
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 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.";
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Operation not supported.": 100,
|
||||
"Operation is unsafe.": 101,
|
||||
"User-defined: {0}": 102,
|
||||
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 200,
|
||||
"Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.": 201,
|
||||
"Type '{0}' cannot be changed to type '{1}'.": 202,
|
||||
@ -29,7 +30,6 @@
|
||||
"A rest parameter must be last in a parameter list.": 1014,
|
||||
"Parameter cannot have question mark and initializer.": 1015,
|
||||
"A required parameter cannot follow an optional parameter.": 1016,
|
||||
"Enum member must have initializer.": 1061,
|
||||
"Statements are not allowed in ambient contexts.": 1036,
|
||||
"Initializers are not allowed in ambient contexts.": 1039,
|
||||
"'{0}' modifier cannot be used here.": 1042,
|
||||
@ -38,6 +38,7 @@
|
||||
"A 'set' accessor must have exactly one parameter.": 1049,
|
||||
"A 'set' accessor parameter cannot have an initializer.": 1052,
|
||||
"A 'get' accessor cannot have parameters.": 1054,
|
||||
"Enum member must have initializer.": 1061,
|
||||
"Type parameters cannot appear on a constructor declaration.": 1092,
|
||||
"Type annotation cannot appear on a constructor declaration.": 1093,
|
||||
"An accessor cannot have type parameters.": 1094,
|
||||
@ -99,6 +100,7 @@
|
||||
"Individual declarations in merged declaration '{0}' must be all exported or all local.": 2395,
|
||||
"Type '{0}' has no property '{1}'.": 2460,
|
||||
"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,
|
||||
"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,
|
||||
|
9
src/glue/js/binaryen.d.ts
vendored
9
src/glue/js/binaryen.d.ts
vendored
@ -2,5 +2,10 @@
|
||||
|
||||
/// <reference path="../binaryen.d.ts" />
|
||||
|
||||
declare function allocate_memory(size: usize): usize;
|
||||
declare function free_memory(ptr: usize): void;
|
||||
declare namespace binaryen {
|
||||
class Module {
|
||||
constructor(ref: number);
|
||||
emitText(): string;
|
||||
emitAsmjs(): string;
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,16 @@
|
||||
// Copy Binaryen exports to global scope
|
||||
|
||||
const binaryen = global.Binaryen || require("binaryen");
|
||||
global.binaryen = binaryen;
|
||||
|
||||
for (var key in binaryen)
|
||||
if (key.startsWith("_Binaryen") || key.startsWith("_Relooper"))
|
||||
global[key] = binaryen[key];
|
||||
for (var key in binaryen) {
|
||||
if (key.startsWith("_Binaryen") || key.startsWith("_Relooper")) global[key] = binaryen[key];
|
||||
}
|
||||
|
||||
// Utilize Binaryen's heap
|
||||
|
||||
global.allocate_memory = function(size) {
|
||||
if (!size) return 0; // should be safe in our case
|
||||
return binaryen._malloc(size);
|
||||
};
|
||||
|
||||
global.free_memory = function(ptr) {
|
||||
if (ptr) binaryen._free(ptr);
|
||||
};
|
||||
|
||||
global.move_memory = function(dest, src, n) {
|
||||
return binaryen._memmove(dest, src, n);
|
||||
};
|
||||
|
||||
global.store = function(ptr, val) {
|
||||
binaryen.HEAPU8[ptr] = val;
|
||||
};
|
||||
|
||||
global.load = function(ptr) {
|
||||
return binaryen.HEAPU8[ptr];
|
||||
};
|
||||
|
||||
// Implement module stubs
|
||||
|
||||
const Module = require("../../module").Module;
|
||||
|
||||
Module.prototype.toText = function() {
|
||||
return new binaryen.Module(this.ref).emitText();
|
||||
};
|
||||
|
||||
Module.prototype.toAsmjs = function() {
|
||||
return new binaryen.Module(this.ref).emitAsmjs();
|
||||
};
|
||||
global.__memory_allocate = binaryen._malloc;
|
||||
global.__memory_free = binaryen._free;
|
||||
global.__memory_copy = binaryen._memmove;
|
||||
global.__store = function(ptr, val) { binaryen.HEAPU8[ptr] = val; };
|
||||
global.__load = function(ptr) { return binaryen.HEAPU8[ptr]; };
|
||||
|
@ -6,7 +6,17 @@
|
||||
|
||||
/// <reference path="./node.d.ts" />
|
||||
|
||||
import "./binaryen"; // must be first so portable can pick up the memory implementation
|
||||
import "../../../std/portable/index";
|
||||
import "./binaryen";
|
||||
import "./float";
|
||||
import "./i64";
|
||||
|
||||
import { Module } from "../../module";
|
||||
|
||||
Module.prototype.toText = function(this: Module) {
|
||||
return new binaryen.Module(this.ref).emitText();
|
||||
};
|
||||
|
||||
Module.prototype.toAsmjs = function(this: Module) {
|
||||
return new binaryen.Module(this.ref).emitAsmjs();
|
||||
};
|
||||
|
175
src/module.ts
175
src/module.ts
@ -251,7 +251,7 @@ export class Module {
|
||||
static create(): Module {
|
||||
var module = new Module();
|
||||
module.ref = _BinaryenModuleCreate();
|
||||
module.cachedByValue = allocate_memory(16);
|
||||
module.cachedByValue = memory.allocate(16);
|
||||
return module;
|
||||
}
|
||||
|
||||
@ -260,10 +260,10 @@ export class Module {
|
||||
try {
|
||||
let module = new Module();
|
||||
module.ref = _BinaryenModuleRead(cArr, buffer.length);
|
||||
module.cachedByValue = allocate_memory(3 * 8); // LLVM C-ABI, max used is 3 * usize
|
||||
module.cachedByValue = memory.allocate(3 * 8); // LLVM C-ABI, max used is 3 * usize
|
||||
return module;
|
||||
} finally {
|
||||
free_memory(changetype<usize>(cArr));
|
||||
memory.free(changetype<usize>(cArr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,8 +281,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes ? paramTypes.length : 0);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes ? paramTypes.length : 0);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
memory.free(cArr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ export class Module {
|
||||
try {
|
||||
_BinaryenRemoveFunctionType(this.ref, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,8 +360,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<ExpressionRef[]>operands).length : 0);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,7 +387,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenGetGlobal(this.ref, cStr, type);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,7 +485,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenSetGlobal(this.ref, cStr, value);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,8 +499,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,7 +513,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenBreak(this.ref, cStr, condition, value);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,7 +531,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenLoop(this.ref, cStr, body);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -577,9 +577,9 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenSwitch(this.ref, cArr, numNames, cStr, condition, value);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
free_memory(cArr);
|
||||
for (let i = numNames - 1; i >= 0; --i) free_memory(strs[i]);
|
||||
memory.free(cStr);
|
||||
memory.free(cArr);
|
||||
for (let i = numNames - 1; i >= 0; --i) memory.free(strs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -593,8 +593,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -608,8 +608,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,8 +623,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenCallIndirect(this.ref, index, cArr, operands && operands.length || 0, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
free_memory(cArr);
|
||||
memory.free(cStr);
|
||||
memory.free(cArr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,7 +644,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,8 +659,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes ? varTypes.length : 0, body);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -669,7 +669,7 @@ export class Module {
|
||||
try {
|
||||
_BinaryenRemoveFunction(this.ref, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,7 +685,7 @@ export class Module {
|
||||
let typeRef = _BinaryenAddFunctionType(this.ref, tempName, result, cArr, paramTypes ? paramTypes.length : 0);
|
||||
return _BinaryenAddFunction(this.ref, tempName, typeRef, 0, 0, body);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
memory.free(cArr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,8 +705,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddFunctionExport(this.ref, cStr1, cStr2);
|
||||
} finally {
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,8 +719,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddTableExport(this.ref, cStr1, cStr2);
|
||||
} finally {
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,8 +733,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddMemoryExport(this.ref, cStr1, cStr2);
|
||||
} finally {
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,8 +747,8 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddGlobalExport(this.ref, cStr1, cStr2);
|
||||
} finally {
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +757,7 @@ export class Module {
|
||||
try {
|
||||
_BinaryenRemoveExport(this.ref, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,9 +773,9 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddFunctionImport(this.ref, cStr1, cStr2, cStr3, functionType);
|
||||
} finally {
|
||||
free_memory(cStr3);
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr3);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,9 +790,9 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddTableImport(this.ref, cStr1, cStr2, cStr3);
|
||||
} finally {
|
||||
free_memory(cStr3);
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr3);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -807,9 +807,9 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddMemoryImport(this.ref, cStr1, cStr2, cStr3);
|
||||
} finally {
|
||||
free_memory(cStr3);
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr3);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,9 +825,9 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenAddGlobalImport(this.ref, cStr1, cStr2, cStr3, globalType);
|
||||
} finally {
|
||||
free_memory(cStr3);
|
||||
free_memory(cStr2);
|
||||
free_memory(cStr1);
|
||||
memory.free(cStr3);
|
||||
memory.free(cStr2);
|
||||
memory.free(cStr1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,7 +836,7 @@ export class Module {
|
||||
try {
|
||||
_BinaryenRemoveImport(this.ref, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,11 +867,11 @@ export class Module {
|
||||
try {
|
||||
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
|
||||
} finally {
|
||||
free_memory(cArr3);
|
||||
free_memory(cArr2);
|
||||
free_memory(cArr1);
|
||||
for (let i = k - 1; i >= 0; --i) free_memory(segs[i]);
|
||||
free_memory(cStr);
|
||||
memory.free(cArr3);
|
||||
memory.free(cArr2);
|
||||
memory.free(cArr1);
|
||||
for (let i = k - 1; i >= 0; --i) memory.free(segs[i]);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,7 +880,7 @@ export class Module {
|
||||
try {
|
||||
_BinaryenSetFunctionTable(this.ref, cArr, funcs.length);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
memory.free(cArr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -888,10 +888,18 @@ export class Module {
|
||||
_BinaryenSetStart(this.ref, func);
|
||||
}
|
||||
|
||||
getOptimizeLevel(): i32 {
|
||||
return _BinaryenGetOptimizeLevel();
|
||||
}
|
||||
|
||||
setOptimizeLevel(level: i32 = 2): void {
|
||||
_BinaryenSetOptimizeLevel(level);
|
||||
}
|
||||
|
||||
getShrinkLevel(): i32 {
|
||||
return _BinaryenGetShrinkLevel();
|
||||
}
|
||||
|
||||
setShrinkLevel(level: i32 = 1): void {
|
||||
_BinaryenSetShrinkLevel(level);
|
||||
}
|
||||
@ -922,17 +930,26 @@ export class Module {
|
||||
_BinaryenModuleRunPasses(this.ref, cArr, numNames);
|
||||
}
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
for (let i = numNames; i >= 0; --i) {
|
||||
free_memory(names[i]);
|
||||
}
|
||||
memory.free(cArr);
|
||||
for (let i = numNames; i >= 0; --i) memory.free(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private cachedPrecomputeName: usize = 0;
|
||||
private cachedPrecomputeName: usize = 0; // for free'ing
|
||||
private cachedPrecomputeNames: usize = 0;
|
||||
|
||||
precomputeFunction(func: FunctionRef): void {
|
||||
precomputeExpression(expr: ExpressionRef): ExpressionRef {
|
||||
// remember the previous optimize levels and set to max instead, to be sure
|
||||
var previousOptimizeLevel = _BinaryenGetOptimizeLevel();
|
||||
var previousShrinkLevel = _BinaryenGetShrinkLevel();
|
||||
var previousDebugInfo = _BinaryenGetDebugInfo();
|
||||
_BinaryenSetOptimizeLevel(4);
|
||||
_BinaryenSetShrinkLevel(0);
|
||||
_BinaryenSetDebugInfo(false);
|
||||
|
||||
// wrap the expression in a temp. function and run the precompute pass on it
|
||||
var type = _BinaryenExpressionGetType(expr);
|
||||
var func = this.addTemporaryFunction(type, null, expr);
|
||||
var names = this.cachedPrecomputeNames;
|
||||
if (!names) {
|
||||
let name = allocString("precompute");
|
||||
@ -940,6 +957,14 @@ export class Module {
|
||||
this.cachedPrecomputeNames = names = allocI32Array([ name ]);
|
||||
}
|
||||
_BinaryenFunctionRunPasses(func, this.ref, names, 1);
|
||||
expr = _BinaryenFunctionGetBody(func);
|
||||
this.removeTemporaryFunction();
|
||||
|
||||
// reset optimize levels to previous
|
||||
_BinaryenSetOptimizeLevel(previousOptimizeLevel);
|
||||
_BinaryenSetShrinkLevel(previousShrinkLevel);
|
||||
_BinaryenSetDebugInfo(previousDebugInfo);
|
||||
return expr;
|
||||
}
|
||||
|
||||
validate(): bool {
|
||||
@ -965,9 +990,9 @@ export class Module {
|
||||
ret.sourceMap = readString(sourceMapPtr);
|
||||
return ret;
|
||||
} finally {
|
||||
if (cStr) free_memory(cStr);
|
||||
if (binaryPtr) free_memory(binaryPtr);
|
||||
if (sourceMapPtr) free_memory(sourceMapPtr);
|
||||
if (cStr) memory.free(cStr);
|
||||
if (binaryPtr) memory.free(binaryPtr);
|
||||
if (sourceMapPtr) memory.free(sourceMapPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,10 +1006,10 @@ export class Module {
|
||||
|
||||
dispose(): void {
|
||||
assert(this.ref);
|
||||
free_memory(this.cachedByValue);
|
||||
free_memory(this.cachedTemporaryName);
|
||||
free_memory(this.cachedPrecomputeName);
|
||||
free_memory(this.cachedPrecomputeNames);
|
||||
memory.free(this.cachedByValue);
|
||||
memory.free(this.cachedTemporaryName);
|
||||
memory.free(this.cachedPrecomputeName);
|
||||
memory.free(this.cachedPrecomputeNames);
|
||||
_BinaryenModuleDispose(this.ref);
|
||||
this.ref = 0;
|
||||
}
|
||||
@ -1086,7 +1111,7 @@ export class Module {
|
||||
try {
|
||||
return _BinaryenModuleAddDebugInfoFileName(this.ref, cStr);
|
||||
} finally {
|
||||
free_memory(cStr);
|
||||
memory.free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1336,7 +1361,7 @@ export class Relooper {
|
||||
try {
|
||||
_RelooperAddBranchForSwitch(from, to, cArr, indexes.length, code);
|
||||
} finally {
|
||||
free_memory(cArr);
|
||||
memory.free(cArr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1390,7 +1415,7 @@ export class Relooper {
|
||||
function allocU8Array(u8s: Uint8Array | null): usize {
|
||||
if (!u8s) return 0;
|
||||
var numValues = u8s.length;
|
||||
var ptr = allocate_memory(numValues);
|
||||
var ptr = memory.allocate(numValues);
|
||||
var idx = ptr;
|
||||
for (let i = 0; i < numValues; ++i) {
|
||||
store<u8>(idx++, u8s[i]);
|
||||
@ -1400,7 +1425,7 @@ function allocU8Array(u8s: Uint8Array | null): usize {
|
||||
|
||||
function allocI32Array(i32s: i32[] | null): usize {
|
||||
if (!i32s) return 0;
|
||||
var ptr = allocate_memory(i32s.length << 2);
|
||||
var ptr = memory.allocate(i32s.length << 2);
|
||||
var idx = ptr;
|
||||
for (let i = 0, k = i32s.length; i < k; ++i) {
|
||||
let val = i32s[i];
|
||||
@ -1444,7 +1469,7 @@ function stringLengthUTF8(str: string): usize {
|
||||
|
||||
function allocString(str: string | null): usize {
|
||||
if (str == null) return 0;
|
||||
var ptr = allocate_memory(stringLengthUTF8(str) + 1);
|
||||
var ptr = memory.allocate(stringLengthUTF8(str) + 1);
|
||||
// the following is based on Emscripten's stringToUTF8Array
|
||||
var idx = ptr;
|
||||
for (let i = 0, k = str.length; i < k; ++i) {
|
||||
|
155
src/program.ts
155
src/program.ts
@ -132,6 +132,12 @@ class TypeAlias {
|
||||
type: CommonTypeNode;
|
||||
}
|
||||
|
||||
/** Represents a module-level export. */
|
||||
class ModuleExport {
|
||||
element: Element;
|
||||
identifier: IdentifierExpression;
|
||||
}
|
||||
|
||||
/** Represents the kind of an operator overload. */
|
||||
export enum OperatorKind {
|
||||
INVALID,
|
||||
@ -319,7 +325,7 @@ export class Program extends DiagnosticEmitter {
|
||||
/** File-level exports by exported name. */
|
||||
fileLevelExports: Map<string,Element> = new Map();
|
||||
/** Module-level exports by exported name. */
|
||||
moduleLevelExports: Map<string,Element> = new Map();
|
||||
moduleLevelExports: Map<string,ModuleExport> = new Map();
|
||||
|
||||
/** Array prototype reference. */
|
||||
arrayPrototype: ClassPrototype | null = null;
|
||||
@ -329,6 +335,10 @@ export class Program extends DiagnosticEmitter {
|
||||
startFunction: FunctionPrototype;
|
||||
/** Main function reference, if present. */
|
||||
mainFunction: FunctionPrototype | null = null;
|
||||
/** Abort function reference, if present. */
|
||||
abortInstance: Function | null = null;
|
||||
/** Memory allocation function. */
|
||||
memoryAllocateInstance: Function | null = null;
|
||||
|
||||
/** Currently processing filespace. */
|
||||
currentFilespace: Filespace;
|
||||
@ -569,52 +579,54 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
// set up global aliases
|
||||
var globalAliases = options.globalAliases;
|
||||
if (globalAliases) {
|
||||
for (let [alias, name] of globalAliases) {
|
||||
if (!name.length) continue; // explicitly disabled
|
||||
let element = this.elementsLookup.get(name);
|
||||
if (element) this.elementsLookup.set(alias, element);
|
||||
else throw new Error("element not found: " + name);
|
||||
{
|
||||
let globalAliases = options.globalAliases;
|
||||
if (globalAliases) {
|
||||
for (let [alias, name] of globalAliases) {
|
||||
if (!name.length) continue; // explicitly disabled
|
||||
let element = this.elementsLookup.get(name);
|
||||
if (element) this.elementsLookup.set(alias, element);
|
||||
else throw new Error("element not found: " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register 'Array'
|
||||
var arrayPrototype = this.elementsLookup.get("Array");
|
||||
if (arrayPrototype) {
|
||||
assert(arrayPrototype.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
this.arrayPrototype = <ClassPrototype>arrayPrototype;
|
||||
if (this.elementsLookup.has("Array")) {
|
||||
let element = assert(this.elementsLookup.get("Array"));
|
||||
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
this.arrayPrototype = <ClassPrototype>element;
|
||||
}
|
||||
|
||||
// register 'String'
|
||||
var stringPrototype = this.elementsLookup.get("String");
|
||||
if (stringPrototype) {
|
||||
assert(stringPrototype.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
let stringInstance = resolver.resolveClass(<ClassPrototype>stringPrototype, null);
|
||||
if (stringInstance) {
|
||||
if (this.elementsLookup.has("String")) {
|
||||
let element = assert(this.elementsLookup.get("String"));
|
||||
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
let instance = resolver.resolveClass(<ClassPrototype>element, null);
|
||||
if (instance) {
|
||||
if (this.typesLookup.has("string")) {
|
||||
let declaration = (<ClassPrototype>stringPrototype).declaration;
|
||||
let declaration = (<ClassPrototype>element).declaration;
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, declaration.programLevelInternalName
|
||||
);
|
||||
} else {
|
||||
this.stringInstance = stringInstance;
|
||||
this.typesLookup.set("string", stringInstance.type);
|
||||
this.stringInstance = instance;
|
||||
this.typesLookup.set("string", instance.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register 'start'
|
||||
{
|
||||
let element = <Element>assert(this.elementsLookup.get("start"));
|
||||
let element = assert(this.elementsLookup.get("start"));
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
this.startFunction = <FunctionPrototype>element;
|
||||
}
|
||||
|
||||
// register 'main' if present
|
||||
if (this.moduleLevelExports.has("main")) {
|
||||
let element = <Element>this.moduleLevelExports.get("main");
|
||||
if (this.elementsLookup.has("main")) {
|
||||
let element = <Element>this.elementsLookup.get("main");
|
||||
if (
|
||||
element.kind == ElementKind.FUNCTION_PROTOTYPE &&
|
||||
!(<FunctionPrototype>element).isAny(CommonFlags.GENERIC | CommonFlags.AMBIENT)
|
||||
@ -623,6 +635,28 @@ export class Program extends DiagnosticEmitter {
|
||||
this.mainFunction = <FunctionPrototype>element;
|
||||
}
|
||||
}
|
||||
|
||||
// register 'abort' if present
|
||||
if (this.elementsLookup.has("abort")) {
|
||||
let element = <Element>this.elementsLookup.get("abort");
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let instance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
||||
if (instance) this.abortInstance = instance;
|
||||
}
|
||||
|
||||
// register 'memory.allocate' if present
|
||||
if (this.elementsLookup.has("memory")) {
|
||||
let element = <Element>this.elementsLookup.get("memory");
|
||||
let members = element.members;
|
||||
if (members) {
|
||||
if (members.has("allocate")) {
|
||||
element = assert(members.get("allocate"));
|
||||
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let instance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
|
||||
if (instance) this.memoryAllocateInstance = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets a constant integer value. */
|
||||
@ -824,14 +858,18 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, prototype);
|
||||
if (prototype.is(CommonFlags.EXPORT) && declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
this.error(
|
||||
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
return;
|
||||
}
|
||||
prototype.set(CommonFlags.MODULE_EXPORT);
|
||||
this.moduleLevelExports.set(simpleName, prototype);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element: prototype,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1269,14 +1307,18 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, element);
|
||||
if (declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
this.error(
|
||||
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
return;
|
||||
}
|
||||
element.set(CommonFlags.MODULE_EXPORT);
|
||||
this.moduleLevelExports.set(simpleName, element);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1332,31 +1374,39 @@ export class Program extends DiagnosticEmitter {
|
||||
private setExportAndCheckLibrary(
|
||||
internalName: string,
|
||||
element: Element,
|
||||
identifier: IdentifierExpression
|
||||
externalIdentifier: IdentifierExpression
|
||||
): void {
|
||||
// add to file-level exports
|
||||
this.fileLevelExports.set(internalName, element);
|
||||
|
||||
// add to filespace
|
||||
var internalPath = identifier.range.source.internalPath;
|
||||
var internalPath = externalIdentifier.range.source.internalPath;
|
||||
var prefix = FILESPACE_PREFIX + internalPath;
|
||||
var filespace = this.elementsLookup.get(prefix);
|
||||
if (!filespace) filespace = assert(this.elementsLookup.get(prefix + PATH_DELIMITER + "index"));
|
||||
assert(filespace.kind == ElementKind.FILESPACE);
|
||||
var simpleName = identifier.text;
|
||||
var simpleName = externalIdentifier.text;
|
||||
(<Filespace>filespace).members.set(simpleName, element);
|
||||
|
||||
// add global alias if from a library file
|
||||
if (identifier.range.source.isLibrary) {
|
||||
// add global alias if a top-level export of a library file
|
||||
var source = externalIdentifier.range.source;
|
||||
if (source.isLibrary) {
|
||||
if (this.elementsLookup.has(simpleName)) {
|
||||
this.error(
|
||||
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
|
||||
identifier.range, simpleName
|
||||
externalIdentifier.range, simpleName
|
||||
);
|
||||
} else {
|
||||
element.internalName = simpleName;
|
||||
this.elementsLookup.set(simpleName, element);
|
||||
}
|
||||
|
||||
// add module level export if a top-level export of an entry file
|
||||
} else if (source.isEntry) {
|
||||
this.moduleLevelExports.set(externalIdentifier.text, <ModuleExport>{
|
||||
element,
|
||||
identifier: externalIdentifier
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1382,10 +1432,10 @@ export class Program extends DiagnosticEmitter {
|
||||
referencedName = member.range.source.internalPath + PATH_DELIMITER + member.name.text;
|
||||
|
||||
// resolve right away if the element exists
|
||||
if (referencedElement = this.elementsLookup.get(referencedName)) {
|
||||
if (this.elementsLookup.has(referencedName)) {
|
||||
this.setExportAndCheckLibrary(
|
||||
externalName,
|
||||
referencedElement,
|
||||
<Element>this.elementsLookup.get(referencedName),
|
||||
member.externalName
|
||||
);
|
||||
return;
|
||||
@ -1526,14 +1576,18 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, prototype);
|
||||
if (declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
return;
|
||||
}
|
||||
prototype.set(CommonFlags.MODULE_EXPORT);
|
||||
this.moduleLevelExports.set(simpleName, prototype);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element: prototype,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1687,14 +1741,18 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, prototype);
|
||||
if (declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
return;
|
||||
}
|
||||
prototype.set(CommonFlags.MODULE_EXPORT);
|
||||
this.moduleLevelExports.set(simpleName, prototype);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element: prototype,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1772,15 +1830,19 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, namespace);
|
||||
if (declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
if (this.moduleLevelExports.get(simpleName) !== namespace) { // not merged
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
if (existingExport.element !== namespace) { // not merged
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.moduleLevelExports.set(simpleName, namespace);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element: namespace,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
namespace.set(CommonFlags.MODULE_EXPORT);
|
||||
}
|
||||
@ -1869,6 +1931,7 @@ export class Program extends DiagnosticEmitter {
|
||||
decorators
|
||||
? this.checkDecorators(decorators,
|
||||
DecoratorFlags.GLOBAL |
|
||||
DecoratorFlags.INLINE |
|
||||
DecoratorFlags.EXTERNAL
|
||||
)
|
||||
: DecoratorFlags.NONE
|
||||
@ -1904,14 +1967,18 @@ export class Program extends DiagnosticEmitter {
|
||||
this.currentFilespace.members.set(simpleName, global);
|
||||
if (declaration.range.source.isEntry) {
|
||||
if (this.moduleLevelExports.has(simpleName)) {
|
||||
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
|
||||
declaration.name.range, existingExport.element.internalName
|
||||
);
|
||||
continue;
|
||||
}
|
||||
global.set(CommonFlags.MODULE_EXPORT);
|
||||
this.moduleLevelExports.set(simpleName, global);
|
||||
this.moduleLevelExports.set(simpleName, <ModuleExport>{
|
||||
element: global,
|
||||
identifier: declaration.name
|
||||
});
|
||||
}
|
||||
}
|
||||
this.checkGlobal(global, declaration);
|
||||
|
Reference in New Issue
Block a user