[wasm] Support validation of asm.js modules with != 3 args.

Our previous per-arch instantiation thunks for asm.js
didn't support modules that had or were called with anything other
than 3 arguments. Adding support for this.

Addding a runtime test method to check if asm validation succeeded.

Adding a test of validation with different argument count combinations.

R=mstarzinger@chromium.org
TEST=mjsunit/asm/asm-validator.js
BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203

Review-Url: https://codereview.chromium.org/2229723002
Cr-Commit-Position: refs/heads/master@{#38688}
This commit is contained in:
bradnelson 2016-08-17 10:22:09 -07:00 committed by Commit bot
parent b12669b0e2
commit d0e52555f0
16 changed files with 502 additions and 48 deletions

View File

@ -1449,6 +1449,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ Move(r4, r0);
// Push the number of arguments to the callee.
__ SmiTag(r0);
__ push(r0);
@ -1459,17 +1461,40 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
// The function.
__ push(r1);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r4);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmp(r4, Operand(j));
__ b(ne, &over);
}
for (int i = j - 1; i >= 0; --i) {
__ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r4);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(r0, &failed);
__ Drop(2);
__ pop(r4);
__ SmiUntag(r4);
scope.GenerateLeaveFrame();
__ Drop(4);
__ add(r4, r4, Operand(1));
__ Drop(r4);
__ Ret();
__ bind(&failed);

View File

@ -1446,23 +1446,48 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ Move(x4, x0);
// Push a copy of the target function and the new target.
__ SmiTag(x0);
// Push another copy as a parameter to the runtime call.
__ Push(x0, x1, x3, x1);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ ldr(x4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(x4);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmp(x4, Operand(j));
__ B(ne, &over);
}
for (int i = j - 1; i >= 0; --i) {
__ ldr(x4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(x4);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(x0, &failed);
__ Drop(2);
__ pop(x4);
__ SmiUntag(x4);
scope.GenerateLeaveFrame();
__ Drop(4);
__ add(x4, x4, Operand(1));
__ Drop(x4);
__ Ret();
__ bind(&failed);

View File

@ -1010,6 +1010,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ mov(ecx, eax);
// Push the number of arguments to the callee.
__ SmiTag(eax);
__ push(eax);
@ -1020,16 +1022,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
// The function.
__ push(edi);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ push(Operand(
ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmp(ecx, Immediate(j));
__ j(not_equal, &over, Label::kNear);
}
for (int i = j - 1; i >= 0; --i) {
__ Push(Operand(
ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done, Label::kNear);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(eax, &failed, Label::kNear);
__ Drop(2);
__ Pop(ecx);
__ SmiUntag(ecx);
scope.GenerateLeaveFrame();
__ ret(4 * kPointerSize);
__ PopReturnAddressTo(ebx);
__ inc(ecx);
__ lea(esp, Operand(esp, ecx, times_pointer_size, 0));
__ PushReturnAddressFrom(ebx);
__ ret(0);
__ bind(&failed);
// Restore target function and new target.

View File

@ -1444,23 +1444,47 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ Move(t4, a0);
// Push a copy of the target function and the new target.
// Push function as parameter to the runtime call.
__ SmiTag(a0);
__ Push(a0, a1, a3, a1);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(a3);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ Branch(&over, ne, t4, Operand(j));
}
for (int i = j - 1; i >= 0; --i) {
__ lw(t4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(t4);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(a0, &failed);
__ JumpIfSmi(v0, &failed);
__ Drop(2);
__ pop(t4);
__ SmiUntag(t4);
scope.GenerateLeaveFrame();
__ Drop(4);
__ Addu(t4, t4, Operand(1));
__ Lsa(sp, sp, t4, kPointerSizeLog2);
__ Ret();
__ bind(&failed);

View File

@ -1438,21 +1438,44 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
// Push a copy of the target function and the new target.
// Push function as parameter to the runtime call.
__ Move(t2, a0);
__ SmiTag(a0);
__ Push(a0, a1, a3, a1);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(a3);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ Branch(&over, ne, t2, Operand(j));
}
for (int i = j - 1; i >= 0; --i) {
__ lw(t2, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(t2);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(a0, &failed);
__ JumpIfSmi(v0, &failed);
__ Drop(2);
__ pop(t2);
__ SmiUntag(t2);
scope.GenerateLeaveFrame();
__ Drop(4);
__ Addu(t2, t2, Operand(1));
__ Lsa(sp, sp, t2, kPointerSizeLog2);
__ Ret();
__ bind(&failed);

View File

@ -1458,23 +1458,47 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ Move(r5, r3);
// Push a copy of the target function and the new target.
// Push function as parameter to the runtime call.
__ SmiTag(r3);
__ Push(r3, r4, r6, r4);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ LoadP(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r4);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmpi(r5, Operand(j));
__ bne(&over);
}
for (int i = j - 1; i >= 0; --i) {
__ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r5);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(r3, &failed);
__ Drop(2);
__ pop(r5);
__ SmiUntag(r5);
scope.GenerateLeaveFrame();
__ Drop(4);
__ Drop(r5);
__ Ret();
__ bind(&failed);

View File

@ -1465,23 +1465,48 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ Move(r4, r2);
// Push a copy of the target function and the new target.
__ SmiTag(r2);
// Push another copy as a parameter to the runtime call.
__ Push(r2, r3, r5, r3);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ LoadP(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r4);
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ CmpP(r4, Operand(j));
__ b(ne, &over);
}
for (int i = j - 1; i >= 0; --i) {
__ LoadP(r9, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
i * kPointerSize));
__ push(r9);
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(r2, &failed);
__ Drop(2);
__ pop(r4);
__ SmiUntag(r4);
scope.GenerateLeaveFrame();
__ Drop(4);
__ AddP(r4, r4, Operand(1));
__ Drop(r4, r7);
__ Ret();
__ bind(&failed);

View File

@ -1062,6 +1062,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ movp(kScratchRegister, rax);
// Push the number of arguments to the callee.
__ Integer32ToSmi(rax, rax);
__ Push(rax);
@ -1072,16 +1074,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
// The function.
__ Push(rdi);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ Push(Operand(
rbp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmpp(kScratchRegister, Immediate(j));
__ j(not_equal, &over, Label::kNear);
}
for (int i = j - 1; i >= 0; --i) {
__ Push(Operand(
rbp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done, Label::kNear);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(rax, &failed, Label::kNear);
__ Drop(2);
__ Pop(kScratchRegister);
__ SmiToInteger32(kScratchRegister, kScratchRegister);
scope.GenerateLeaveFrame();
__ ret(4 * kPointerSize);
__ PopReturnAddressTo(rbx);
__ incp(kScratchRegister);
__ leap(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0));
__ PushReturnAddressFrom(rbx);
__ ret(0);
__ bind(&failed);
// Restore target function and new target.

View File

@ -1011,6 +1011,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
Label failed;
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve argument count for later compare.
__ mov(ecx, eax);
// Push the number of arguments to the callee.
__ SmiTag(eax);
__ push(eax);
@ -1021,16 +1023,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) {
// The function.
__ push(edi);
// Copy arguments from caller (stdlib, foreign, heap).
for (int i = 2; i >= 0; --i) {
__ push(Operand(
ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
Label args_done;
for (int j = 0; j < 4; ++j) {
Label over;
if (j < 3) {
__ cmp(ecx, Immediate(j));
__ j(not_equal, &over, Label::kNear);
}
for (int i = j - 1; i >= 0; --i) {
__ Push(Operand(
ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize));
}
for (int i = 0; i < 3 - j; ++i) {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
if (j < 3) {
__ jmp(&args_done, Label::kNear);
__ bind(&over);
}
}
__ bind(&args_done);
// Call runtime, on success unwind frame, and parent frame.
__ CallRuntime(Runtime::kInstantiateAsmJs, 4);
// A smi 0 is returned on failure, an object on success.
__ JumpIfSmi(eax, &failed, Label::kNear);
__ Drop(2);
__ Pop(ecx);
__ SmiUntag(ecx);
scope.GenerateLeaveFrame();
__ ret(4 * kPointerSize);
__ PopReturnAddressTo(ebx);
__ inc(ecx);
__ lea(esp, Operand(esp, ecx, times_pointer_size, 0));
__ PushReturnAddressFrom(ebx);
__ ret(0);
__ bind(&failed);
// Restore target function and new target.

View File

@ -476,7 +476,8 @@ int CodeAndMetadataSize(CompilationInfo* info) {
bool GenerateUnoptimizedCode(CompilationInfo* info) {
bool success;
EnsureFeedbackMetadata(info);
if (FLAG_validate_asm && info->scope()->asm_module()) {
if (FLAG_validate_asm && info->scope()->asm_module() &&
!info->shared_info()->is_asm_wasm_broken()) {
MaybeHandle<FixedArray> wasm_data;
wasm_data = AsmJs::ConvertAsmToWasm(info->parse_info());
if (!wasm_data.is_null()) {
@ -1436,7 +1437,9 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
// TODO(4280): For now we play it safe and remove the bytecode array when we
// switch to baseline code. We might consider keeping around the bytecode so
// that it can be used as the "source of truth" eventually.
if (!FLAG_ignition_preserve_bytecode) shared->ClearBytecodeArray();
if (shared->HasBytecodeArray()) {
if (!FLAG_ignition_preserve_bytecode) shared->ClearBytecodeArray();
}
// The scope info might not have been set if a lazily compiled
// function is inlined before being called for the first time.

View File

@ -6091,6 +6091,8 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_setter_function,
kIsSetterFunction)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_default_constructor,
kIsDefaultConstructor)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_asm_wasm_broken,
kIsAsmWasmBroken)
inline bool SharedFunctionInfo::is_resumable() const {
return is_generator() || is_async();

View File

@ -7307,6 +7307,9 @@ class SharedFunctionInfo: public HeapObject {
// Whether this function was created from a FunctionDeclaration.
DECL_BOOLEAN_ACCESSORS(is_declaration)
// Indicates that asm->wasm conversion failed and should not be re-attempted.
DECL_BOOLEAN_ACCESSORS(is_asm_wasm_broken)
inline FunctionKind kind();
inline void set_kind(FunctionKind kind);
@ -7579,6 +7582,7 @@ class SharedFunctionInfo: public HeapObject {
kIsAsyncFunction,
kDeserialized,
kIsDeclaration,
kIsAsmWasmBroken,
kCompilerHintsCount, // Pseudo entry
};
// Add hints for other modes when they're added.

View File

@ -101,8 +101,18 @@ RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) {
return *result.ToHandleChecked();
}
}
// Remove wasm data and return a smi 0 to indicate failure.
// Remove wasm data, mark as broken for asm->wasm,
// replace code with CompileLazy, and return a smi 0 to indicate failure.
function->shared()->ClearAsmWasmData();
function->shared()->set_is_asm_wasm_broken(true);
DCHECK(function->code() ==
isolate->builtins()->builtin(Builtins::kInstantiateAsmJs));
function->ReplaceCode(isolate->builtins()->builtin(Builtins::kCompileLazy));
if (function->shared()->code() ==
isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) {
function->shared()->ReplaceCode(
isolate->builtins()->builtin(Builtins::kCompileLazy));
}
return Smi::FromInt(0);
}

View File

@ -662,6 +662,22 @@ RUNTIME_FUNCTION(Runtime_InNewSpace) {
return isolate->heap()->ToBoolean(isolate->heap()->InNewSpace(obj));
}
RUNTIME_FUNCTION(Runtime_IsAsmWasmCode) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
if (!function->shared()->HasAsmWasmData()) {
// Doesn't have wasm data.
return isolate->heap()->ToBoolean(false);
}
if (function->shared()->code() !=
isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) {
// Hasn't been compiled yet.
return isolate->heap()->ToBoolean(false);
}
return isolate->heap()->ToBoolean(true);
}
#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
RUNTIME_FUNCTION(Runtime_Has##Name) { \

View File

@ -885,7 +885,8 @@ namespace internal {
F(HasFixedUint8ClampedElements, 1, 1) \
F(SpeciesProtector, 0, 1) \
F(SerializeWasmModule, 1, 1) \
F(DeserializeWasmModule, 1, 1)
F(DeserializeWasmModule, 1, 1) \
F(IsAsmWasmCode, 1, 1)
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \
F(ArrayBufferGetByteLength, 1, 1) \

View File

@ -0,0 +1,188 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --validate-asm --allow-natives-syntax
function IsAlwaysOpt(module) {
return %GetOptimizationStatus(module) === 3;
}
(function TestModuleArgs() {
function Module1(stdlib) {
"use asm";
function foo() { }
return { foo: foo };
}
function Module2(stdlib, ffi) {
"use asm";
function foo() { }
return { foo: foo };
}
function Module3(stdlib, ffi, heap) {
"use asm";
function foo() { }
return { foo: foo };
}
var modules = [Module1, Module2, Module3];
var heap = new ArrayBuffer(1024 * 1024);
for (var i = 0; i < modules.length; ++i) {
print('Module' + (i + 1));
var module = modules[i];
// TODO(bradnelson): Support modules without the stdlib.
var m = module({});
assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module));
var m = module({}, {});
assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module));
var m = module({}, {}, heap);
assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module));
var m = module({}, {}, heap, {});
assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module));
}
})();
(function TestBadModule() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { var y = 3; var x = 1 + y; return 123; }
return { foo: foo };
}
var m = Module({});
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestBadArgTypes() {
function Module(a, b, c) {
"use asm";
return {};
}
var m = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
assertEquals({}, m);
})();
(function TestBadArgTypesMismatch() {
function Module(a, b, c) {
"use asm";
return {};
}
var m = Module(1, 2);
assertFalse(%IsAsmWasmCode(Module));
assertEquals({}, m);
})();
(function TestModuleNoStdlib() {
// TODO(bradnelson):
// Support modules like this if they don't use the whole stdlib.
function Module() {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m = Module({});
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestModuleWith5() {
function Module(a, b, c, d, e) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var heap = new ArrayBuffer(1024 * 1024);
var m = Module({}, {}, heap);
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestModuleNoStdlibCall() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
// TODO(bradnelson): Support instantiation like this if stdlib is unused.
var m = Module();
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestModuleNew() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m = new Module({}, {});
assertTrue(%IsAsmWasmCode(Module) || IsAlwaysOpt(Module));
assertEquals(123, m.foo());
})();
(function TestMultipleFailures() {
function Module(stdlib) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m1 = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
var m2 = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestFailureThenSuccess() {
function MkModule() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
return Module;
}
var Module1 = MkModule();
var Module2 = MkModule();
var heap = new ArrayBuffer(1024 * 1024);
var m1 = Module1(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module1));
var m2 = Module2({}, {}, heap);
assertFalse(%IsAsmWasmCode(Module2));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestSuccessThenFailure() {
function MkModule() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
return Module;
}
var Module1 = MkModule();
var Module2 = MkModule();
var heap = new ArrayBuffer(1024 * 1024);
var m1 = Module1({}, {}, heap);
assertTrue(%IsAsmWasmCode(Module1) || IsAlwaysOpt(Module1));
var m2 = Module2(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module2));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestBoundFunction() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var heap = new ArrayBuffer(1024 * 1024);
var ModuleBound = Module.bind(this, {}, {}, heap);
var m = ModuleBound();
assertTrue(%IsAsmWasmCode(Module) || IsAlwaysOpt(Module));
assertEquals(123, m.foo());
})();