Commit 8d491089 authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] Allow import function to be any kind of callables.

With this CL all kinds of Callable can imported into wasm. Please take a special look at the context that is used now in the WasmToJSWrapper.

BUG=633895
TEST=mjsunit/wasm/ffi.js

Review-Url: https://codereview.chromium.org/2208703002
Cr-Commit-Position: refs/heads/master@{#38569}
parent 3cc4e25c
......@@ -2182,7 +2182,7 @@ Node* WasmGraphBuilder::BuildChangeFloat64ToTagged(Node* value) {
return value;
}
Node* WasmGraphBuilder::ToJS(Node* node, Node* context, wasm::LocalType type) {
Node* WasmGraphBuilder::ToJS(Node* node, wasm::LocalType type) {
switch (type) {
case wasm::kAstI32:
return BuildChangeInt32ToTagged(node);
......@@ -2515,18 +2515,18 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
retval = graph()->NewNode(jsgraph()->common()->Projection(0), retval,
graph()->start());
}
Node* jsval =
ToJS(retval, context,
sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
Node* jsval = ToJS(
retval, sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
Node* ret =
graph()->NewNode(jsgraph()->common()->Return(), jsval, call, start);
MergeControlToEnd(jsgraph(), ret);
}
void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target,
wasm::FunctionSig* sig) {
int js_count = function->shared()->internal_formal_parameter_count();
DCHECK(target->IsCallable());
int wasm_count = static_cast<int>(sig->parameter_count());
int param_count;
if (jsgraph()->machine()->Is64()) {
......@@ -2541,61 +2541,71 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
Node* start = Start(param_count + 3);
*effect_ = start;
*control_ = start;
// JS context is the last parameter.
Node* context = HeapConstant(Handle<Context>(function->context(), isolate));
Node** args = Buffer(wasm_count + 7);
bool arg_count_before_args = false;
bool add_new_target_undefined = false;
// The default context of the target.
Handle<Context> target_context = isolate->native_context();
// Optimization: check if the target is a JSFunction with the right arity so
// that we can call it directly.
bool call_direct = false;
int pos = 0;
if (js_count == wasm_count) {
// exact arity match, just call the function directly.
desc = Linkage::GetJSCallDescriptor(graph()->zone(), false, wasm_count + 1,
CallDescriptor::kNoFlags);
arg_count_before_args = false;
add_new_target_undefined = true;
} else {
// Use the Call builtin.
if (target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(target);
if (function->shared()->internal_formal_parameter_count() == wasm_count) {
call_direct = true;
args[pos++] = jsgraph()->Constant(target); // target callable.
// Receiver.
if (is_sloppy(function->shared()->language_mode()) &&
!function->shared()->native()) {
args[pos++] =
HeapConstant(handle(function->context()->global_proxy(), isolate));
} else {
args[pos++] = jsgraph()->Constant(
handle(isolate->heap()->undefined_value(), isolate));
}
desc = Linkage::GetJSCallDescriptor(
graph()->zone(), false, wasm_count + 1, CallDescriptor::kNoFlags);
// For a direct call we have to use the context of the JSFunction.
target_context = handle(function->context());
}
}
// We cannot call the target directly, we have to use the Call builtin.
if (!call_direct) {
Callable callable = CodeFactory::Call(isolate);
args[pos++] = jsgraph()->HeapConstant(callable.code());
args[pos++] = jsgraph()->Constant(target); // target callable
args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
args[pos++] = jsgraph()->Constant(
handle(isolate->heap()->undefined_value(), isolate)); // receiver
desc = Linkage::GetStubCallDescriptor(isolate, graph()->zone(),
callable.descriptor(), wasm_count + 1,
CallDescriptor::kNoFlags);
arg_count_before_args = true;
}
args[pos++] = jsgraph()->Constant(function); // JS function.
if (arg_count_before_args) {
args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
}
// Create the receiver constant (either undefined or the global proxy).
Handle<Object> receiver(isolate->heap()->undefined_value(), isolate);
if (is_sloppy(function->shared()->language_mode())) {
receiver = Handle<Object>(function->context()->global_proxy(), isolate);
}
args[pos++] = jsgraph()->Constant(receiver);
// Convert WASM numbers to JS values.
int param_index = 0;
for (int i = 0; i < wasm_count; ++i) {
Node* param =
graph()->NewNode(jsgraph()->common()->Parameter(param_index++), start);
args[pos++] = ToJS(param, context, sig->GetParam(i));
args[pos++] = ToJS(param, sig->GetParam(i));
if (jsgraph()->machine()->Is32() && sig->GetParam(i) == wasm::kAstI64) {
// On 32 bit platforms we have to skip the high word of int64 parameters.
param_index++;
}
}
if (add_new_target_undefined) {
if (call_direct) {
args[pos++] = jsgraph()->UndefinedConstant(); // new target
}
if (!arg_count_before_args) {
args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
}
args[pos++] = context;
args[pos++] = HeapConstant(target_context);
args[pos++] = *effect_;
args[pos++] = *control_;
......@@ -2604,7 +2614,7 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
// Convert the return value back.
Node* ret;
Node* val =
FromJS(call, context,
FromJS(call, HeapConstant(isolate->native_context()),
sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
if (jsgraph()->machine()->Is32() && sig->return_count() > 0 &&
sig->GetReturn() == wasm::kAstI64) {
......@@ -2960,8 +2970,7 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::ModuleEnv* module,
return code;
}
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
Handle<JSFunction> function,
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function) {
......@@ -2980,7 +2989,7 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
WasmGraphBuilder builder(&zone, &jsgraph, sig);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmToJSWrapper(function, sig);
builder.BuildWasmToJSWrapper(target, sig);
Handle<Code> code = Handle<Code>::null();
{
......
......@@ -81,8 +81,7 @@ class WasmCompilationUnit final {
};
// Wraps a JS function, producing a code object that can be called from WASM.
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
Handle<JSFunction> function,
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function);
......@@ -156,10 +155,9 @@ class WasmGraphBuilder {
Node* CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSFunction> function,
wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig);
Node* ToJS(Node* node, Node* context, wasm::LocalType type);
Node* ToJS(Node* node, wasm::LocalType type);
Node* FromJS(Node* node, Node* context, wasm::LocalType type);
Node* Invert(Node* node);
Node* FunctionTable(uint32_t index);
......
......@@ -261,6 +261,34 @@ RUNTIME_FUNCTION(Runtime_GetUndetectable) {
return *Utils::OpenHandle(*obj);
}
static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) {
double v1 = args[0]
->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToChecked();
double v2 = args[1]
->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToChecked();
args.GetReturnValue().Set(
v8::Number::New(v8::Isolate::GetCurrent(), v1 - v2));
}
// Returns a callable object. The object returns the difference of its two
// parameters when it is called.
RUNTIME_FUNCTION(Runtime_GetCallable) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(v8_isolate);
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(call_as_function);
v8_isolate->GetCurrentContext();
Local<v8::Object> instance =
t->GetFunction(v8_isolate->GetCurrentContext())
.ToLocalChecked()
->NewInstance(v8_isolate->GetCurrentContext())
.ToLocalChecked();
return *Utils::OpenHandle(*instance);
}
RUNTIME_FUNCTION(Runtime_ClearFunctionTypeFeedback) {
HandleScope scope(isolate);
......
......@@ -842,6 +842,7 @@ namespace internal {
F(UnblockConcurrentRecompilation, 0, 1) \
F(GetOptimizationCount, 1, 1) \
F(GetUndetectable, 0, 1) \
F(GetCallable, 0, 1) \
F(ClearFunctionTypeFeedback, 1, 1) \
F(CheckWasmWrapperElision, 2, 1) \
F(NotifyContextDisposed, 0, 1) \
......
......@@ -471,7 +471,7 @@ static MaybeHandle<JSFunction> ReportFFIError(
return MaybeHandle<JSFunction>();
}
static MaybeHandle<JSFunction> LookupFunction(
static MaybeHandle<JSReceiver> LookupFunction(
ErrorThrower& thrower, Factory* factory, Handle<JSReceiver> ffi,
uint32_t index, Handle<String> module_name,
MaybeHandle<String> function_name) {
......@@ -509,12 +509,12 @@ static MaybeHandle<JSFunction> LookupFunction(
function = module;
}
if (!function->IsJSFunction()) {
return ReportFFIError(thrower, "not a function", index, module_name,
if (!function->IsCallable()) {
return ReportFFIError(thrower, "not a callable", index, module_name,
function_name);
}
return Handle<JSFunction>::cast(function);
return Handle<JSReceiver>::cast(function);
}
namespace {
......@@ -661,23 +661,27 @@ bool CompileWrappersToImportedFunctions(Isolate* isolate,
int param_count = sig_data_size - ret_count;
CHECK(param_count >= 0);
MaybeHandle<JSFunction> function = LookupFunction(
MaybeHandle<JSReceiver> function = LookupFunction(
*thrower, isolate->factory(), ffi, index, module_name, function_name);
if (function.is_null()) return false;
Handle<Code> code;
Handle<JSFunction> func = function.ToHandleChecked();
Handle<Code> export_wrapper_code = handle(func->code());
Handle<JSReceiver> target = function.ToHandleChecked();
bool isMatch = false;
if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
int exported_param_count =
Smi::cast(func->GetInternalField(kInternalArity))->value();
Handle<ByteArray> exportedSig = Handle<ByteArray>(
ByteArray::cast(func->GetInternalField(kInternalSignature)));
if (exported_param_count == param_count &&
exportedSig->length() == sig_data->length() &&
memcmp(exportedSig->data(), sig_data->data(),
exportedSig->length()) == 0) {
isMatch = true;
Handle<Code> export_wrapper_code;
if (target->IsJSFunction()) {
Handle<JSFunction> func = Handle<JSFunction>::cast(target);
export_wrapper_code = handle(func->code());
if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
int exported_param_count =
Smi::cast(func->GetInternalField(kInternalArity))->value();
Handle<ByteArray> exportedSig = Handle<ByteArray>(
ByteArray::cast(func->GetInternalField(kInternalSignature)));
if (exported_param_count == param_count &&
exportedSig->length() == sig_data->length() &&
memcmp(exportedSig->data(), sig_data->data(),
exportedSig->length()) == 0) {
isMatch = true;
}
}
}
if (isMatch) {
......@@ -704,7 +708,7 @@ bool CompileWrappersToImportedFunctions(Isolate* isolate,
sizeof(MachineRepresentation) * sig_data_size);
FunctionSig sig(ret_count, param_count, reps);
code = compiler::CompileWasmToJSWrapper(isolate, func, &sig, index,
code = compiler::CompileWasmToJSWrapper(isolate, target, &sig, index,
module_name, function_name);
}
imports.push_back(code);
......
......@@ -97,6 +97,7 @@ void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc, double a,
} // namespace
TEST(Run_Int32Sub_jswrapped) {
CcTest::InitializeVM();
TestSignatures sigs;
TestingModule module;
WasmFunctionCompiler t(sigs.i_ii(), &module);
......@@ -108,6 +109,7 @@ TEST(Run_Int32Sub_jswrapped) {
}
TEST(Run_Float32Div_jswrapped) {
CcTest::InitializeVM();
TestSignatures sigs;
TestingModule module;
WasmFunctionCompiler t(sigs.f_ff(), &module);
......@@ -119,6 +121,7 @@ TEST(Run_Float32Div_jswrapped) {
}
TEST(Run_Float64Add_jswrapped) {
CcTest::InitializeVM();
TestSignatures sigs;
TestingModule module;
WasmFunctionCompiler t(sigs.d_dd(), &module);
......@@ -130,6 +133,7 @@ TEST(Run_Float64Add_jswrapped) {
}
TEST(Run_I32Popcount_jswrapped) {
CcTest::InitializeVM();
TestSignatures sigs;
TestingModule module;
WasmFunctionCompiler t(sigs.i_i(), &module);
......@@ -142,6 +146,7 @@ TEST(Run_I32Popcount_jswrapped) {
}
TEST(Run_CallJS_Add_jswrapped) {
CcTest::InitializeVM();
TestSignatures sigs;
TestingModule module;
WasmFunctionCompiler t(sigs.i_i(), &module);
......@@ -191,21 +196,45 @@ void RunJSSelectTest(int which) {
}
}
TEST(Run_JSSelect_0) { RunJSSelectTest(0); }
TEST(Run_JSSelect_0) {
CcTest::InitializeVM();
RunJSSelectTest(0);
}
TEST(Run_JSSelect_1) { RunJSSelectTest(1); }
TEST(Run_JSSelect_1) {
CcTest::InitializeVM();
RunJSSelectTest(1);
}
TEST(Run_JSSelect_2) { RunJSSelectTest(2); }
TEST(Run_JSSelect_2) {
CcTest::InitializeVM();
RunJSSelectTest(2);
}
TEST(Run_JSSelect_3) { RunJSSelectTest(3); }
TEST(Run_JSSelect_3) {
CcTest::InitializeVM();
RunJSSelectTest(3);
}
TEST(Run_JSSelect_4) { RunJSSelectTest(4); }
TEST(Run_JSSelect_4) {
CcTest::InitializeVM();
RunJSSelectTest(4);
}
TEST(Run_JSSelect_5) { RunJSSelectTest(5); }
TEST(Run_JSSelect_5) {
CcTest::InitializeVM();
RunJSSelectTest(5);
}
TEST(Run_JSSelect_6) { RunJSSelectTest(6); }
TEST(Run_JSSelect_6) {
CcTest::InitializeVM();
RunJSSelectTest(6);
}
TEST(Run_JSSelect_7) { RunJSSelectTest(7); }
TEST(Run_JSSelect_7) {
CcTest::InitializeVM();
RunJSSelectTest(7);
}
void RunWASMSelectTest(int which) {
PredictableInputValues inputs(0x200);
......@@ -238,21 +267,45 @@ void RunWASMSelectTest(int which) {
}
}
TEST(Run_WASMSelect_0) { RunWASMSelectTest(0); }
TEST(Run_WASMSelect_0) {
CcTest::InitializeVM();
RunWASMSelectTest(0);
}
TEST(Run_WASMSelect_1) { RunWASMSelectTest(1); }
TEST(Run_WASMSelect_1) {
CcTest::InitializeVM();
RunWASMSelectTest(1);
}
TEST(Run_WASMSelect_2) { RunWASMSelectTest(2); }
TEST(Run_WASMSelect_2) {
CcTest::InitializeVM();
RunWASMSelectTest(2);
}
TEST(Run_WASMSelect_3) { RunWASMSelectTest(3); }
TEST(Run_WASMSelect_3) {
CcTest::InitializeVM();
RunWASMSelectTest(3);
}
TEST(Run_WASMSelect_4) { RunWASMSelectTest(4); }
TEST(Run_WASMSelect_4) {
CcTest::InitializeVM();
RunWASMSelectTest(4);
}
TEST(Run_WASMSelect_5) { RunWASMSelectTest(5); }
TEST(Run_WASMSelect_5) {
CcTest::InitializeVM();
RunWASMSelectTest(5);
}
TEST(Run_WASMSelect_6) { RunWASMSelectTest(6); }
TEST(Run_WASMSelect_6) {
CcTest::InitializeVM();
RunWASMSelectTest(6);
}
TEST(Run_WASMSelect_7) { RunWASMSelectTest(7); }
TEST(Run_WASMSelect_7) {
CcTest::InitializeVM();
RunWASMSelectTest(7);
}
void RunWASMSelectAlignTest(int num_args, int num_params) {
PredictableInputValues inputs(0x300);
......@@ -288,37 +341,44 @@ void RunWASMSelectAlignTest(int num_args, int num_params) {
}
TEST(Run_WASMSelectAlign_0) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(0, 1);
RunWASMSelectAlignTest(0, 2);
}
TEST(Run_WASMSelectAlign_1) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(1, 2);
RunWASMSelectAlignTest(1, 3);
}
TEST(Run_WASMSelectAlign_2) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(2, 3);
RunWASMSelectAlignTest(2, 4);
}
TEST(Run_WASMSelectAlign_3) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(3, 3);
RunWASMSelectAlignTest(3, 4);
}
TEST(Run_WASMSelectAlign_4) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(4, 3);
RunWASMSelectAlignTest(4, 4);
}
TEST(Run_WASMSelectAlign_7) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(7, 5);
RunWASMSelectAlignTest(7, 6);
RunWASMSelectAlignTest(7, 7);
}
TEST(Run_WASMSelectAlign_8) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(8, 5);
RunWASMSelectAlignTest(8, 6);
RunWASMSelectAlignTest(8, 7);
......@@ -326,6 +386,7 @@ TEST(Run_WASMSelectAlign_8) {
}
TEST(Run_WASMSelectAlign_9) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(9, 6);
RunWASMSelectAlignTest(9, 7);
RunWASMSelectAlignTest(9, 8);
......@@ -333,6 +394,7 @@ TEST(Run_WASMSelectAlign_9) {
}
TEST(Run_WASMSelectAlign_10) {
CcTest::InitializeVM();
RunWASMSelectAlignTest(10, 7);
RunWASMSelectAlignTest(10, 8);
RunWASMSelectAlignTest(10, 9);
......@@ -394,31 +456,37 @@ void RunJSSelectAlignTest(int num_args, int num_params) {
}
TEST(Run_JSSelectAlign_0) {
CcTest::InitializeVM();
RunJSSelectAlignTest(0, 1);
RunJSSelectAlignTest(0, 2);
}
TEST(Run_JSSelectAlign_1) {
CcTest::InitializeVM();
RunJSSelectAlignTest(1, 2);
RunJSSelectAlignTest(1, 3);
}
TEST(Run_JSSelectAlign_2) {
CcTest::InitializeVM();
RunJSSelectAlignTest(2, 3);
RunJSSelectAlignTest(2, 4);
}
TEST(Run_JSSelectAlign_3) {
CcTest::InitializeVM();
RunJSSelectAlignTest(3, 3);
RunJSSelectAlignTest(3, 4);
}
TEST(Run_JSSelectAlign_4) {
CcTest::InitializeVM();
RunJSSelectAlignTest(4, 3);
RunJSSelectAlignTest(4, 4);
}
TEST(Run_JSSelectAlign_7) {
CcTest::InitializeVM();
RunJSSelectAlignTest(7, 3);
RunJSSelectAlignTest(7, 4);
RunJSSelectAlignTest(7, 4);
......@@ -426,6 +494,7 @@ TEST(Run_JSSelectAlign_7) {
}
TEST(Run_JSSelectAlign_8) {
CcTest::InitializeVM();
RunJSSelectAlignTest(8, 5);
RunJSSelectAlignTest(8, 6);
RunJSSelectAlignTest(8, 7);
......@@ -433,6 +502,7 @@ TEST(Run_JSSelectAlign_8) {
}
TEST(Run_JSSelectAlign_9) {
CcTest::InitializeVM();
RunJSSelectAlignTest(9, 6);
RunJSSelectAlignTest(9, 7);
RunJSSelectAlignTest(9, 8);
......@@ -440,6 +510,7 @@ TEST(Run_JSSelectAlign_9) {
}
TEST(Run_JSSelectAlign_10) {
CcTest::InitializeVM();
RunJSSelectAlignTest(10, 7);
RunJSSelectAlignTest(10, 8);
RunJSSelectAlignTest(10, 9);
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm
// Flags: --expose-wasm --allow-natives-syntax
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
......@@ -25,7 +25,9 @@ function testCallFFI(func, check) {
for (var i = 0; i < 100000; i += 10003) {
var a = 22.5 + i, b = 10.5 + i;
var r = main(a, b);
check(r, a, b);
if (check) {
check(r, a, b);
}
}
}
......@@ -52,8 +54,63 @@ function check_FOREIGN_SUB(r, a, b) {
was_called = false;
}
// Test calling a normal JSFunction.
print("JSFunction");
testCallFFI(FOREIGN_SUB, check_FOREIGN_SUB);
// Test calling a proxy.
print("Proxy");
var proxy_sub = new Proxy(FOREIGN_SUB, {});
testCallFFI(proxy_sub, check_FOREIGN_SUB);
// Test calling a bind function.
print("Bind function");
var bind_sub = FOREIGN_SUB.bind();
testCallFFI(bind_sub, check_FOREIGN_SUB);
var main_for_constructor_test;
print("Constructor");
(function testCallConstructor() {
class C {}
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_dd);
builder.addImport("func", sig_index);
builder.addFunction("main", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallImport, kArity2, 0 // --
]) // --
.exportFunc();
main_for_constructor_test = builder.instantiate({func: C}).exports.main;
assertThrows("main_for_constructor_test(12, 43)", TypeError);
}) ();
print("Native function");
(function test_ffi_call_to_native() {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_d);
builder.addImport("func", sig_index);
builder.addFunction("main", sig_index)
.addBody([
kExprCallImport, kArity0, 0 // --
]) // --
.exportFunc();
var main = builder.instantiate({func: Object.prototype.toString}).exports.main;
// The result of the call to Object.prototype.toString should be
// [object Undefined]. However, we cannot test for this result because wasm
// cannot return objects but converts them to float64 in this test.
assertEquals(NaN, main());
})();
print("Callable JSObject");
testCallFFI(%GetCallable(), function check(r, a, b) {assertEquals(a - b, r);});
function FOREIGN_ABCD(a, b, c, d) {
print("FOREIGN_ABCD(" + a + ", " + b + ", " + c + ", " + d + ")");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment