Commit 57b9a3b1 authored by titzer's avatar titzer Committed by Commit Bot

[wasm] Fix user properties for exported wasm functions and add extensive tests.

R=ishell@chromium.org,clemensh@chromium.org
BUG=chromium:742659

Review-Url: https://codereview.chromium.org/2977113002
Cr-Commit-Position: refs/heads/master@{#46772}
parent 3e47cb87
......@@ -390,7 +390,6 @@ enum ContextLookupFlags {
V(STRING_ITERATOR_MAP_INDEX, Map, string_iterator_map) \
V(SYMBOL_FUNCTION_INDEX, JSFunction, symbol_function) \
V(NATIVE_FUNCTION_MAP_INDEX, Map, native_function_map) \
V(WASM_FUNCTION_MAP_INDEX, Map, wasm_function_map) \
V(WASM_INSTANCE_CONSTRUCTOR_INDEX, JSFunction, wasm_instance_constructor) \
V(WASM_MEMORY_CONSTRUCTOR_INDEX, JSFunction, wasm_memory_constructor) \
V(WASM_MODULE_CONSTRUCTOR_INDEX, JSFunction, wasm_module_constructor) \
......
......@@ -1504,7 +1504,6 @@ Handle<JSFunction> Factory::NewFunction(Handle<Map> map, Handle<String> name,
(*map == *isolate()->sloppy_function_with_readonly_prototype_map()) ||
(*map == *isolate()->strict_function_map()) ||
(*map == *isolate()->strict_function_without_prototype_map()) ||
(*map == *isolate()->wasm_function_map()) ||
(*map == *isolate()->native_function_map()));
}
#endif
......
......@@ -242,6 +242,8 @@
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
V(wasm_function_index_symbol) \
V(wasm_instance_symbol) \
V(uninitialized_symbol)
#define PUBLIC_SYMBOL_LIST(V) \
......
......@@ -865,33 +865,15 @@ Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object,
void WasmJs::Install(Isolate* isolate) {
Handle<JSGlobalObject> global = isolate->global_object();
Handle<Context> context(global->native_context(), isolate);
// Check if the map is already installed and do nothing otherwise.
if (context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) return;
// Install Maps.
Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
InstanceType instance_type = prev_map->instance_type();
int embedder_fields = JSObject::GetEmbedderFieldCount(*prev_map);
CHECK_EQ(0, embedder_fields);
int pre_allocated =
prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
int instance_size = 0;
int in_object_properties = WasmExportedFunction::kFieldCount;
JSFunction::CalculateInstanceSizeHelper(instance_type, embedder_fields,
in_object_properties, &instance_size,
&in_object_properties);
int unused_property_fields = in_object_properties - pre_allocated;
Handle<Map> map = Map::CopyInitialMap(
prev_map, instance_size, in_object_properties, unused_property_fields);
context->set_wasm_function_map(*map);
// Install the JS API once only.
Object* prev = context->get(Context::WASM_MODULE_CONSTRUCTOR_INDEX);
if (!prev->IsUndefined(isolate)) {
DCHECK(prev->IsJSFunction());
return;
}
Factory* factory = isolate->factory();
// Install the JS API.
// Setup WebAssembly
Handle<String> name = v8_str(isolate, "WebAssembly");
Handle<JSFunction> cons = factory->NewFunction(isolate->strict_function_map(),
......
......@@ -485,18 +485,46 @@ uint32_t WasmInstanceObject::GetMaxMemoryPages() {
return FLAG_wasm_max_mem_pages;
}
bool WasmExportedFunction::IsWasmExportedFunction(Object* object) {
if (!object->IsJSFunction()) return false;
Handle<JSFunction> js_function(JSFunction::cast(object));
if (Code::JS_TO_WASM_FUNCTION != js_function->code()->kind()) return false;
Handle<Symbol> symbol(
js_function->GetIsolate()->factory()->wasm_instance_symbol());
MaybeHandle<Object> maybe_result =
JSObject::GetPropertyOrElement(js_function, symbol);
Handle<Object> result;
if (!maybe_result.ToHandle(&result)) return false;
return result->IsWasmInstanceObject();
}
WasmExportedFunction* WasmExportedFunction::cast(Object* object) {
DCHECK(object && object->IsJSFunction());
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION,
JSFunction::cast(object)->code()->kind());
// TODO(titzer): brand check for WasmExportedFunction.
DCHECK(IsWasmExportedFunction(object));
return reinterpret_cast<WasmExportedFunction*>(object);
}
WasmInstanceObject* WasmExportedFunction::instance() {
DisallowHeapAllocation no_allocation;
Handle<Symbol> symbol(GetIsolate()->factory()->wasm_instance_symbol());
MaybeHandle<Object> result =
JSObject::GetPropertyOrElement(handle(this), symbol);
return WasmInstanceObject::cast(*(result.ToHandleChecked()));
}
int WasmExportedFunction::function_index() {
DisallowHeapAllocation no_allocation;
Handle<Symbol> symbol = GetIsolate()->factory()->wasm_function_index_symbol();
MaybeHandle<Object> result =
JSObject::GetPropertyOrElement(handle(this), symbol);
return result.ToHandleChecked()->Number();
}
Handle<WasmExportedFunction> WasmExportedFunction::New(
Isolate* isolate, Handle<WasmInstanceObject> instance,
MaybeHandle<String> maybe_name, int func_index, int arity,
Handle<Code> export_wrapper) {
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
Handle<String> name;
if (!maybe_name.ToHandle(&name)) {
EmbeddedVector<char, 16> buffer;
......@@ -506,22 +534,23 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
Vector<uint8_t>::cast(buffer.SubVector(0, length)))
.ToHandleChecked();
}
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name, export_wrapper, false);
shared->set_length(arity);
shared->set_internal_formal_parameter_count(arity);
Handle<JSFunction> js_function = isolate->factory()->NewFunction(
isolate->wasm_function_map(), name, export_wrapper);
isolate->sloppy_function_map(), name, export_wrapper);
Handle<WasmExportedFunction> function(
reinterpret_cast<WasmExportedFunction*>(*js_function), isolate);
js_function->set_shared(*shared);
Handle<Symbol> instance_symbol(isolate->factory()->wasm_instance_symbol());
JSObject::AddProperty(js_function, instance_symbol, instance, DONT_ENUM);
function->set_shared(*shared);
function->set_instance(*instance);
function->set_function_index(func_index);
Handle<Symbol> function_index_symbol(
isolate->factory()->wasm_function_index_symbol());
JSObject::AddProperty(js_function, function_index_symbol,
isolate->factory()->NewNumber(func_index), DONT_ENUM);
return Handle<WasmExportedFunction>::cast(function);
return Handle<WasmExportedFunction>::cast(js_function);
}
bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) {
......
......@@ -194,21 +194,11 @@ class WasmInstanceObject : public JSObject {
// A WASM function that is wrapped and exported to JavaScript.
class WasmExportedFunction : public JSFunction {
public:
DECL_OOL_QUERY(WasmExportedFunction)
DECL_OOL_CAST(WasmExportedFunction)
WasmInstanceObject* instance();
int function_index();
DECL_ACCESSORS(instance, WasmInstanceObject)
DECL_INT_ACCESSORS(function_index)
enum { // --
kInstanceIndex,
kFunctionIndexIndex,
kFieldCount
};
static const int kSize = JSFunction::kSize + kFieldCount * kPointerSize;
DEF_OFFSET(Instance)
DEF_OFFSET(FunctionIndex)
static WasmExportedFunction* cast(Object* object);
static bool IsWasmExportedFunction(Object* object);
static Handle<WasmExportedFunction> New(Isolate* isolate,
Handle<WasmInstanceObject> instance,
......@@ -699,10 +689,6 @@ ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo, kDebugInfoOffset)
ACCESSORS(WasmInstanceObject, directly_called_instances, FixedArray,
kDirectlyCalledInstancesOffset)
// WasmExportedFunction
ACCESSORS(WasmExportedFunction, instance, WasmInstanceObject, kInstanceOffset)
SMI_ACCESSORS(WasmExportedFunction, function_index, kFunctionIndexOffset)
// WasmSharedModuleData
ACCESSORS(WasmSharedModuleData, module_bytes, SeqOneByteString,
kModuleBytesOffset)
......
// Copyright 2017 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: --expose-wasm --expose-gc --verify-heap
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
const verifyHeap = gc;
let globalCounter = 10000000;
function testProperties(obj) {
for (let i = 0; i < 3; i++) {
obj.x = 1001;
assertEquals(1001, obj.x);
obj.y = "old";
assertEquals("old", obj.y);
delete obj.y;
assertEquals("undefined", typeof obj.y);
let uid = globalCounter++;
let fresh = "f_" + uid;
obj.z = fresh;
assertEquals(fresh, obj.z);
obj[fresh] = uid;
assertEquals(uid, obj[fresh]);
verifyHeap();
assertEquals(1001, obj.x);
assertEquals(fresh, obj.z);
assertEquals(uid, obj[fresh]);
}
// These properties are special for JSFunctions.
Object.defineProperty(obj, 'name', {value: "crazy"});
Object.defineProperty(obj, 'length', {value: 999});
}
function minus18(x) { return x - 18; }
function id(x) { return x; }
function printName(when, f) {
print(" " + when + ": name=" + f.name + ", length=" + f.length);
}
(function ExportedFunctionTest() {
print("ExportedFunctionTest");
print(" instance 1, exporting");
var builder = new WasmModuleBuilder();
builder.addFunction("exp", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0])
.exportAs("exp");
let module1 = builder.toModule();
let instance1 = new WebAssembly.Instance(module1);
let g = instance1.exports.exp;
testProperties(g);
// The WASM-internal fields of {g} are only inspected when {g} is
// used as an import into another instance.
print(" instance 2, importing");
var builder = new WasmModuleBuilder();
builder.addImport("imp", "func", kSig_i_i);
let module2 = builder.toModule();
let instance2 = new WebAssembly.Instance(module2, {imp: {func: g}});
testProperties(g);
})();
(function ImportReexportChain() {
print("ImportReexportChain");
var f = id;
for (let i = 0; i < 5; i++) {
let builder = new WasmModuleBuilder();
builder.addImport("imp", "func", kSig_i_i);
builder.addExport("exp", 0);
let module = builder.toModule();
let instance = new WebAssembly.Instance(module, {imp: {func: f}});
let g = instance.exports.exp;
assertInstanceof(g, Function);
printName("before", g);
testProperties(g);
printName(" after", g);
// The WASM-internal fields of {g} are only inspected when {g} is
// used as an import into another instance. Use {g} as the import
// the next time through the loop.
f = g;
}
})();
(function ModuleTest() {
for (f of [x => (x + 19 + globalCounter), minus18]) {
print("ModuleTest");
let builder = new WasmModuleBuilder();
builder.addImport("m", "f", kSig_i_i);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0])
.exportAs("main");
builder.addMemory(1, 1, false)
.exportMemoryAs("memory")
let module = builder.toModule();
testProperties(module);
for (let i = 0; i < 3; i++) {
print(" instance " + i);
let instance = new WebAssembly.Instance(module, {m: {f: f}});
testProperties(instance);
print(" memory " + i);
let m = instance.exports.memory;
assertInstanceof(m, WebAssembly.Memory);
testProperties(m);
print(" function " + i);
let g = instance.exports.main;
assertInstanceof(g, Function);
printName("before", g);
testProperties(g);
printName(" after", g);
assertInstanceof(g, Function);
testProperties(g);
for (let j = 10; j < 15; j++) {
assertEquals(f(j), g(j));
}
verifyHeap();
// The WASM-internal fields of {g} are only inspected when {g} is
// used as an import into another instance. Use {g} as the import
// the next time through the loop.
f = g;
}
}
})();
(function ConstructedTest() {
print("ConstructedTest");
var memory = undefined, table = undefined;
for (let i = 0; i < 4; i++) {
print(" iteration " + i);
let m = new WebAssembly.Memory({initial: 1});
let t = new WebAssembly.Table({element: "anyfunc", initial: 1});
m.old = memory;
t.old = table;
memory = m;
table = t;
testProperties(memory);
testProperties(table);
}
})();
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