diff --git a/src/framework/Framework.ts b/src/framework/Framework.ts index 38193eb..5a9f89c 100644 --- a/src/framework/Framework.ts +++ b/src/framework/Framework.ts @@ -74,7 +74,7 @@ export class Framework { const result: SuiteResult = new SuiteResult(suite); const first: TestScenario = order[0]; - await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? []).catch((e: Error) => result.error(e.message))); + await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? [], first.compilerOptions).catch((e: Error) => result.error(e.message))); await this.runSuite(result, testee, order); this.reporter.report(result); @@ -100,7 +100,7 @@ export class Framework { const result: SuiteResult = new SuiteResult(suite); const first: TestScenario = order[0]; - await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? []).catch((e: Error) => result.error(e.message))); + await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? [], first.compilerOptions).catch((e: Error) => result.error(e.message))); await this.runSuite(result, testee, order); this.reporter.report(result); @@ -128,7 +128,7 @@ export class Framework { const result: SuiteResult = new SuiteResult(suite); const first: TestScenario = order[i][0]; - await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? []).catch((e: Error) => result.error(e.message))); + await timeout('Initialize testbed', testee.connector.timeout, testee.initialize(first.program, first.args ?? [], first.compilerOptions).catch((e: Error) => result.error(e.message))); for (let j = i; j < order.length; j += suite.testees.length) { await this.runSuite(result, testee, order[j]); diff --git a/src/framework/Testee.ts b/src/framework/Testee.ts index 53dd730..ca4a97a 100644 --- a/src/framework/Testee.ts +++ b/src/framework/Testee.ts @@ -7,7 +7,7 @@ import {Kind} from './scenario/Step'; import {SourceMapFactory} from '../sourcemap/SourceMapFactory'; import {TestScenario} from './scenario/TestScenario'; import {OutofPlaceSpecification, PlatformType, TestbedSpecification} from '../testbeds/TestbedSpecification'; -import {CompileOutput, CompilerFactory} from '../manage/Compiler'; +import {CompileOutput, CompilerFactory, CompilerOptions} from '../manage/Compiler'; import {WABT} from '../util/env'; import {Outcome} from '../reporter/describers/Describer'; import {WASM} from '../sourcemap/Wasm'; @@ -90,11 +90,11 @@ export class Testee { // TODO unified with testbed interface return target == Target.proxy ? this.proxy : this.testbed; } - public async initialize(program: string, args: string[] = []): Promise { + public async initialize(program: string, args: string[] = [], compilerOptions?: CompilerOptions): Promise { return new Promise(async (resolve, reject) => { if (this.specification.type === PlatformType.emu2emu) { const spec = (this.specification as OutofPlaceSpecification).proxy; - const proxy: Testbed | void = await this.connector.initialize(spec, program, args ?? []).catch((e) => { + const proxy: Testbed | void = await this.connector.initialize(spec, program, args ?? [], compilerOptions).catch((e) => { reject(e) }); @@ -109,14 +109,14 @@ export class Testee { // TODO unified with testbed interface await this.proxy.sendRequest(new SourceMap.Mapping(), Message.proxifyRequest); args = args.concat(['--proxy', `${spec.dummy.port}`]); - const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args).catch((e) => { + const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args, compilerOptions).catch((e) => { reject(e); }); if (testbed) { this.testbed = testbed; } } else { - const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args).catch((e) => { + const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args, compilerOptions).catch((e) => { reject(e) }); if (testbed) { @@ -168,12 +168,12 @@ export class Testee { // TODO unified with testbed interface return; } - const compiled: CompileOutput = await new CompilerFactory(WABT).pickCompiler(description.program).compile(description.program); + const compiled: CompileOutput = await new CompilerFactory(WABT).pickCompiler(description.program).compile(description.program, description.compilerOptions); try { await timeout(`uploading module`, testee.timeout, testee.bed()!.sendRequest(new SourceMap.Mapping(), Message.updateModule(compiled.file))).catch((e) => Promise.reject(e)); testee.current = description.program; } catch { - await testee.initialize(description.program, description.args ?? []).catch((o) => Promise.reject(o)); + await testee.initialize(description.program, description.args ?? [], description.compilerOptions).catch((o) => Promise.reject(o)); } }).catch((e: Error | string) => { if (typeof e === 'string') { @@ -184,7 +184,7 @@ export class Testee { // TODO unified with testbed interface }); await this.run('Get source mapping', testee.connector.timeout, async function () { - map = await testee.mapper.map(description.program); + map = await testee.mapper.map(description.program, description.compilerOptions); }).catch((e: Error | string) => { if (typeof e === 'string') { scenarioResult.error(e); @@ -231,7 +231,7 @@ export class Testee { // TODO unified with testbed interface (testee, req, map) => timeout(`sending instruction ${req.type}`, testee.timeout, testee.bed(step.target ?? Target.supervisor)!.sendRequest(map, req)), (testee) => testee.run(`Recover: re-initialize ${testee.testbed?.name}`, testee.connector.timeout, async function () { - await testee.initialize(description.program, description.args ?? []).catch((o) => { + await testee.initialize(description.program, description.args ?? [], description.compilerOptions).catch((o) => { return Promise.reject(o) }); }), 1).catch((e: Error) => { diff --git a/src/framework/scenario/TestScenario.ts b/src/framework/scenario/TestScenario.ts index 8a7b4b0..3469074 100644 --- a/src/framework/scenario/TestScenario.ts +++ b/src/framework/scenario/TestScenario.ts @@ -1,5 +1,6 @@ -import {Breakpoint} from "../../debug/Breakpoint"; -import {Step} from "./Step"; +import { Breakpoint } from "../../debug/Breakpoint"; +import { CompilerOptions } from "../../manage/Compiler"; +import { Step } from "./Step"; /** A series of scenario to perform on a single instance of the vm */ export interface TestScenario { @@ -19,4 +20,6 @@ export interface TestScenario { skip?: boolean; dependencies?: TestScenario[]; + + compilerOptions?: CompilerOptions; } \ No newline at end of file diff --git a/src/manage/Compiler.ts b/src/manage/Compiler.ts index e43e67b..038db3e 100644 --- a/src/manage/Compiler.ts +++ b/src/manage/Compiler.ts @@ -1,12 +1,12 @@ +import { exec, ExecException } from 'child_process'; +import { EventEmitter } from 'events'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import {exec, ExecException} from 'child_process'; -import {SourceMap} from '../sourcemap/SourceMap'; import * as readline from 'readline'; -import {EventEmitter} from 'events'; -import {find, getFileExtension} from '../util/util'; -import {AsScriptMapper} from '../sourcemap/SourceMapper'; +import { SourceMap } from '../sourcemap/SourceMap'; +import { AsScriptMapper } from '../sourcemap/SourceMapper'; +import { find, getFileExtension } from '../util/util'; import SourceLine = SourceMap.SourceLine; enum CompilationEvents { @@ -45,12 +45,16 @@ export class CompilerFactory { } } +export interface CompilerOptions { + disableBulkMemory?: boolean; +} + export abstract class Compiler extends EventEmitter { // compiles program to WAT - abstract compile(program: string): Promise; + abstract compile(program: string, options?: CompilerOptions): Promise; // generates a sourceMap - abstract map(program: string): Promise; + abstract map(program: string, compilerOptions: CompilerOptions): Promise; protected makeTmpDir(): Promise { return new Promise((resolve, reject) => { @@ -75,17 +79,17 @@ export class WatCompiler extends Compiler { this.wabt = wabt.length > 0 ? wabt + "/" : ""; } - public async compile(program: string, dir?: string): Promise { + public async compile(program: string, compilerOptions?: CompilerOptions, dir?: string): Promise { if (dir) { - return this.wasm(program, dir); + return this.wasm(program, compilerOptions, dir); } return this.makeTmpDir().then((dir: string) => { - return this.wasm(program, dir); + return this.wasm(program, compilerOptions, dir); }); } - private wasm(program: string, dir: string): Promise { + private wasm(program: string, compilerOptions?: CompilerOptions, dir?: string): Promise { // do not recompiled previous compilations // if (this.compiled.has(program)) { // return Promise.resolve(this.compiled.get(program)!); @@ -94,7 +98,8 @@ export class WatCompiler extends Compiler { // compile WAT to Wasm return new Promise((resolve, reject) => { const file = `${dir}/upload.wasm`; - const command = `${this.wabt}wat2wasm --no-canonicalize-leb128s --disable-bulk-memory --debug-names -v -o ${file} ${program}`; + const disableBulkMemory = compilerOptions?.disableBulkMemory ?? true; + const command = `${this.wabt}wat2wasm --no-canonicalize-leb128s ${disableBulkMemory ? '--disable-bulk-memory' : ''} --debug-names -v -o ${file} ${program}`; let out: string = ''; let err: string = ''; @@ -139,8 +144,8 @@ export class WatCompiler extends Compiler { }); } - public async map(program: string): Promise { - return this.compile(program).then((output) => { + public async map(program: string, compilerOptions: CompilerOptions): Promise { + return this.compile(program, compilerOptions).then((output) => { return this.dump(output); }); } @@ -160,15 +165,15 @@ export class AsScriptCompiler extends Compiler { this.wabt = wabt; } - public async compile(program: string): Promise { + public async compile(program: string, compilerOptions?: CompilerOptions): Promise { return this.makeTmpDir().then((dir) => { - return this.wasm(program, dir); + return this.wasm(program, dir, compilerOptions); }); } - public async map(program: string): Promise { + public async map(program: string, compilerOptions?: CompilerOptions): Promise { const emitter = this; - return this.compile(program).then(async function (output: CompileOutput) { + return this.compile(program, compilerOptions).then(async function (output: CompileOutput) { output.map = await new AsScriptMapper(program, path.dirname(output.file)).mapping(); emitter.emit(CompilationEvents.sourcemap); return Promise.resolve(output); @@ -202,7 +207,7 @@ export class AsScriptCompiler extends Compiler { }); } - private async wasm(program: string, dir: string): Promise { + private async wasm(program: string, dir: string, compilerOptions?: CompilerOptions): Promise { // do not recompiled previous compilations // if (this.compiled.has(program)) { // return Promise.resolve(this.compiled.get(program)!); @@ -211,7 +216,7 @@ export class AsScriptCompiler extends Compiler { // compile AS to Wasm and WAT return new Promise(async (resolve, reject) => { const file = `${dir}/upload.wasm`; - const command = await this.getCompilationCommand(program, file); + const command = await this.getCompilationCommand(program, file, compilerOptions); let out: string = ''; let err: string = ''; @@ -234,11 +239,12 @@ export class AsScriptCompiler extends Compiler { }); } - private getCompilationCommand(program: string, output: string): Promise { + private getCompilationCommand(program: string, output: string, compilerOptions?: CompilerOptions): Promise { // builds asc command based on the version of asc return new Promise(async (resolve) => { const version: Version = await AsScriptCompiler.retrieveVersion(); - resolve(`npx asc ${program} --exportTable --disable bulk-memory --sourceMap --debug ` + + const disableBulkMemory = compilerOptions?.disableBulkMemory ?? true; + resolve(`npx asc ${program} --exportTable ${disableBulkMemory ? '--disable bulk-memory' : ''} --sourceMap --debug ` + `${(version.major > 0 || +version.minor >= 20) ? '--outFile' : '--binaryFile'} ${output}`); }); } diff --git a/src/messaging/Message.ts b/src/messaging/Message.ts index 82ce7bb..56b682f 100644 --- a/src/messaging/Message.ts +++ b/src/messaging/Message.ts @@ -1,12 +1,12 @@ -import {WARDuino} from '../debug/WARDuino'; -import {ackParser, breakpointParser, invokeParser, stateParser} from './Parsers'; -import {Breakpoint} from '../debug/Breakpoint'; -import {WASM} from '../sourcemap/Wasm'; +import { readFileSync } from 'fs'; import ieee754 from 'ieee754'; -import {SourceMap} from '../sourcemap/SourceMap'; -import {readFileSync} from 'fs'; -import {CompileOutput, CompilerFactory} from '../manage/Compiler'; -import {WABT} from '../util/env'; +import { Breakpoint } from '../debug/Breakpoint'; +import { WARDuino } from '../debug/WARDuino'; +import { CompileOutput, CompilerFactory, CompilerOptions } from '../manage/Compiler'; +import { SourceMap } from '../sourcemap/SourceMap'; +import { WASM } from '../sourcemap/Wasm'; +import { WABT } from '../util/env'; +import { ackParser, breakpointParser, invokeParser, stateParser } from './Parsers'; import Interrupt = WARDuino.Interrupt; import State = WARDuino.State; import Value = WASM.Value; @@ -130,8 +130,8 @@ export namespace Message { } } - export async function uploadFile(program: string): Promise> { - const compiled: CompileOutput = await new CompilerFactory(WABT).pickCompiler(program).compile(program); + export async function uploadFile(program: string, compilerOptions: CompilerOptions): Promise> { + const compiled: CompileOutput = await new CompilerFactory(WABT).pickCompiler(program).compile(program, compilerOptions); return updateModule(compiled.file); } diff --git a/src/sourcemap/SourceMapFactory.ts b/src/sourcemap/SourceMapFactory.ts index 67efb64..543b151 100644 --- a/src/sourcemap/SourceMapFactory.ts +++ b/src/sourcemap/SourceMapFactory.ts @@ -1,5 +1,5 @@ import {getFileExtension} from '../util/util'; -import {CompileOutput, CompilerFactory} from '../manage/Compiler'; +import {CompileOutput, CompilerFactory, CompilerOptions} from '../manage/Compiler'; import {AsScriptMapper, WatMapper} from './SourceMapper'; import {WABT} from '../util/env'; import {SourceMap} from './SourceMap'; @@ -13,12 +13,12 @@ export class SourceMapFactory { this.compilerFactory = new CompilerFactory(WABT); } - public async map(source: string, tmpdir?: string): Promise { + public async map(source: string, compilerOptions?: CompilerOptions, tmpdir?: string): Promise { let compiled: CompileOutput; switch (getFileExtension(source)) { case 'wast' : case 'wat' : - compiled = await this.compilerFactory.pickCompiler(source).compile(source); + compiled = await this.compilerFactory.pickCompiler(source).compile(source, compilerOptions); return new WatMapper(compiled.out ?? '', tmpdir ?? path.dirname(compiled.file), WABT).mapping(); case 'ts' : return new AsScriptMapper(source ?? '', tmpdir ?? path.dirname(source)).mapping(); diff --git a/src/testbeds/TestbedFactory.ts b/src/testbeds/TestbedFactory.ts index 39861a8..ecc2c02 100644 --- a/src/testbeds/TestbedFactory.ts +++ b/src/testbeds/TestbedFactory.ts @@ -1,6 +1,6 @@ import {Testbed} from './Testbed'; import {ARDUINO, EMULATOR, WABT} from '../util/env'; -import {CompileOutput, CompilerFactory} from '../manage/Compiler'; +import {CompileOutput, CompilerFactory, CompilerOptions} from '../manage/Compiler'; import {DummyProxy, Emulator} from './Emulator'; import {UploaderFactory} from '../manage/Uploader'; import {Connection} from '../bridge/Connection'; @@ -20,8 +20,8 @@ export class TestbedFactory { this.uploaderFactory = new UploaderFactory(EMULATOR, ARDUINO); } - public async initialize(specification: TestbedSpecification, program: string, args: string[]): Promise { - const compiled: CompileOutput = await this.compilerFactory.pickCompiler(program).compile(program).catch((e) => Promise.reject(e)); + public async initialize(specification: TestbedSpecification, program: string, args: string[], compilerOptions?: CompilerOptions): Promise { + const compiled: CompileOutput = await this.compilerFactory.pickCompiler(program).compile(program, compilerOptions).catch((e) => Promise.reject(e)); const connection: Connection = await this.uploaderFactory.pickUploader(specification, args).upload(compiled).catch((e) => Promise.reject(e)); switch (specification.type) {