Commit e1677516 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Preserve identity on {WasmJSFunction} re-export.

This preserves the object identity of a {WebAssembly.Function} instance
that is being re-exported by a module. Such functions are considered to
have an internal [[FunctionAddress]] slot and hence require their object
identity to be preserved (similar to {WasmExportedFunction} already).

R=jkummerow@chromium.org
TEST=mjsunit/wasm/type-reflection
BUG=v8:7742

Change-Id: I88ba75fcd91ce04440008467f3b218a1ac3047db
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1763545Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63346}
parent 18a2268f
......@@ -1465,13 +1465,16 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
}
if (WasmExportedFunction::IsWasmExportedFunction(*this)) {
WasmExportedFunction function = WasmExportedFunction::cast(*this);
os << "\n - WASM instance "
<< reinterpret_cast<void*>(function.instance().ptr());
os << "\n - WASM function index " << function.function_index();
os << "\n - WASM instance: " << Brief(function.instance());
os << "\n - WASM function index: " << function.function_index();
}
if (WasmJSFunction::IsWasmJSFunction(*this)) {
WasmJSFunction function = WasmJSFunction::cast(*this);
os << "\n - WASM wrapper around: " << Brief(function.GetCallable());
}
shared().PrintSourceCode(os);
JSObjectPrintBody(os, *this);
os << "\n - feedback vector: ";
os << " - feedback vector: ";
if (!shared().HasFeedbackMetadata()) {
os << "feedback metadata is not available in SFI\n";
} else if (has_feedback_vector()) {
......
......@@ -430,8 +430,8 @@ RUNTIME_FUNCTION(Runtime_WasmRefFunc) {
isolate->set_context(instance->native_context());
CONVERT_UINT32_ARG_CHECKED(function_index, 0);
Handle<WasmExportedFunction> function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance,
Handle<WasmExternalFunction> function =
WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
function_index);
return *function;
......
......@@ -835,6 +835,14 @@ bool InstanceBuilder::ProcessImportedFunction(
module_name, import_name);
return false;
}
// Store any {WasmExternalFunction} callable in the instance before the call
// is resolved to preserve its identity. This handles exported functions as
// well as functions constructed via other means (e.g. WebAssembly.Function).
if (WasmExternalFunction::IsWasmExternalFunction(*value)) {
WasmInstanceObject::SetWasmExternalFunction(
isolate_, instance, func_index,
Handle<WasmExternalFunction>::cast(value));
}
auto js_receiver = Handle<JSReceiver>::cast(value);
FunctionSig* expected_sig = module_->functions[func_index].sig;
auto resolved =
......@@ -855,10 +863,6 @@ bool InstanceBuilder::ProcessImportedFunction(
Address imported_target = imported_function->GetWasmCallTarget();
ImportedFunctionEntry entry(instance, func_index);
entry.SetWasmToWasm(*imported_instance, imported_target);
// Also store the {WasmExportedFunction} in the instance to preserve its
// identity.
WasmInstanceObject::SetWasmExportedFunction(
isolate_, instance, func_index, imported_function);
break;
}
case compiler::WasmImportCallKind::kWasmToCapi: {
......@@ -1373,7 +1377,7 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
break;
case WasmInitExpr::kRefFuncConst: {
DCHECK(enabled_.anyref);
auto function = WasmInstanceObject::GetOrCreateWasmExportedFunction(
auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction(
isolate_, instance, global.init.val.function_index);
tagged_globals_->set(global.offset, *function);
break;
......@@ -1450,10 +1454,10 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
const WasmImport& import = module_->import_table[index];
if (import.kind == kExternalFunction) {
Handle<Object> value = sanitized_imports_[index].value;
if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
WasmInstanceObject::SetWasmExportedFunction(
if (WasmExternalFunction::IsWasmExternalFunction(*value)) {
WasmInstanceObject::SetWasmExternalFunction(
isolate_, instance, import.index,
Handle<WasmExportedFunction>::cast(value));
Handle<WasmExternalFunction>::cast(value));
}
}
}
......@@ -1498,10 +1502,10 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
// TODO(wasm): reduce duplication with LoadElemSegment() further below
MaybeHandle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(
Handle<WasmExternalFunction> wasm_external_function =
WasmInstanceObject::GetOrCreateWasmExternalFunction(
isolate_, instance, exp.index);
desc.set_value(wasm_exported_function.ToHandleChecked());
desc.set_value(wasm_external_function);
if (is_asm_js &&
String::Equals(isolate_, name,
......@@ -1661,27 +1665,27 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
.Set(sig_id, instance, func_index);
}
// For AnyRef tables, we have to generate the WasmExportedFunction eagerly.
// For AnyRef tables, we have to generate the WasmExternalFunction eagerly.
// Later we cannot know if an entry is a placeholder or not.
if (table_object->type() == kWasmAnyRef) {
Handle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance,
Handle<WasmExternalFunction> wasm_external_function =
WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
func_index);
WasmTableObject::Set(isolate, table_object, entry_index,
wasm_exported_function);
wasm_external_function);
} else {
// Update the table object's other dispatch tables.
MaybeHandle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
MaybeHandle<WasmExternalFunction> wasm_external_function =
WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
func_index);
if (wasm_exported_function.is_null()) {
if (wasm_external_function.is_null()) {
// No JSFunction entry yet exists for this function. Create a {Tuple2}
// holding the information to lazily allocate one.
WasmTableObject::SetFunctionTablePlaceholder(
isolate, table_object, entry_index, instance, func_index);
} else {
table_object->entries().set(entry_index,
*wasm_exported_function.ToHandleChecked());
*wasm_external_function.ToHandleChecked());
}
// UpdateDispatchTables() updates all other dispatch tables, since
// we have not yet added the dispatch table we are currently building.
......
......@@ -3046,8 +3046,8 @@ class ThreadImpl {
code->at(pc));
HandleScope handle_scope(isolate_); // Avoid leaking handles.
Handle<WasmExportedFunction> function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(
Handle<WasmExternalFunction> function =
WasmInstanceObject::GetOrCreateWasmExternalFunction(
isolate_, instance_object_, imm.index);
Push(WasmValue(function));
len = 1 + imm.length;
......
......@@ -261,8 +261,8 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, managed_native_allocations, Foreign,
kManagedNativeAllocationsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, exceptions_table, FixedArray,
kExceptionsTableOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_exported_functions, FixedArray,
kWasmExportedFunctionsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_external_functions, FixedArray,
kWasmExternalFunctionsOffset)
void WasmInstanceObject::clear_padding() {
if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
......@@ -363,6 +363,12 @@ ACCESSORS(WasmCapiFunctionData, wrapper_code, Code, kWrapperCodeOffset)
ACCESSORS(WasmCapiFunctionData, serialized_signature, PodArray<wasm::ValueType>,
kSerializedSignatureOffset)
// WasmExternalFunction
WasmExternalFunction::WasmExternalFunction(Address ptr) : JSFunction(ptr) {
SLOW_DCHECK(IsWasmExternalFunction(*this));
}
CAST_ACCESSOR(WasmExternalFunction)
// WasmIndirectFunctionTable
OBJECT_CONSTRUCTORS_IMPL(WasmIndirectFunctionTable, Struct)
CAST_ACCESSOR(WasmIndirectFunctionTable)
......
......@@ -963,7 +963,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
// Check if we already compiled a wrapper for the function but did not store
// it in the table slot yet.
entry = WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance,
entry = WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
function_index);
entries->set(entry_index, *entry);
return entry;
......@@ -1858,27 +1858,27 @@ bool WasmInstanceObject::InitTableEntries(Isolate* isolate,
dst, src, count);
}
MaybeHandle<WasmExportedFunction> WasmInstanceObject::GetWasmExportedFunction(
MaybeHandle<WasmExternalFunction> WasmInstanceObject::GetWasmExternalFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index) {
MaybeHandle<WasmExportedFunction> result;
if (instance->has_wasm_exported_functions()) {
Object val = instance->wasm_exported_functions().get(index);
MaybeHandle<WasmExternalFunction> result;
if (instance->has_wasm_external_functions()) {
Object val = instance->wasm_external_functions().get(index);
if (!val.IsUndefined(isolate)) {
result = Handle<WasmExportedFunction>(WasmExportedFunction::cast(val),
result = Handle<WasmExternalFunction>(WasmExternalFunction::cast(val),
isolate);
}
}
return result;
}
Handle<WasmExportedFunction>
WasmInstanceObject::GetOrCreateWasmExportedFunction(
Handle<WasmExternalFunction>
WasmInstanceObject::GetOrCreateWasmExternalFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index) {
MaybeHandle<WasmExportedFunction> maybe_result =
WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
MaybeHandle<WasmExternalFunction> maybe_result =
WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
function_index);
Handle<WasmExportedFunction> result;
Handle<WasmExternalFunction> result;
if (maybe_result.ToHandle(&result)) {
return result;
}
......@@ -1903,27 +1903,27 @@ WasmInstanceObject::GetOrCreateWasmExportedFunction(
isolate, function.sig, function.imported);
module_object->export_wrappers().set(wrapper_index, *wrapper);
}
result = WasmExportedFunction::New(
result = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
isolate, instance, function_index,
static_cast<int>(function.sig->parameter_count()), wrapper);
static_cast<int>(function.sig->parameter_count()), wrapper));
WasmInstanceObject::SetWasmExportedFunction(isolate, instance, function_index,
WasmInstanceObject::SetWasmExternalFunction(isolate, instance, function_index,
result);
return result;
}
void WasmInstanceObject::SetWasmExportedFunction(
void WasmInstanceObject::SetWasmExternalFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index,
Handle<WasmExportedFunction> val) {
Handle<WasmExternalFunction> val) {
Handle<FixedArray> functions;
if (!instance->has_wasm_exported_functions()) {
// lazily-allocate the wasm exported functions.
if (!instance->has_wasm_external_functions()) {
// Lazily allocate the wasm external functions array.
functions = isolate->factory()->NewFixedArray(
static_cast<int>(instance->module()->functions.size()));
instance->set_wasm_exported_functions(*functions);
instance->set_wasm_external_functions(*functions);
} else {
functions =
Handle<FixedArray>(instance->wasm_exported_functions(), isolate);
Handle<FixedArray>(instance->wasm_external_functions(), isolate);
}
functions->set(index, *val);
}
......@@ -2343,6 +2343,11 @@ PodArray<wasm::ValueType> WasmCapiFunction::GetSerializedSignature() const {
return shared().wasm_capi_function_data().serialized_signature();
}
bool WasmExternalFunction::IsWasmExternalFunction(Object object) {
return WasmExportedFunction::IsWasmExportedFunction(object) ||
WasmJSFunction::IsWasmJSFunction(object);
}
Handle<WasmExceptionTag> WasmExceptionTag::New(Isolate* isolate, int index) {
Handle<WasmExceptionTag> result =
Handle<WasmExceptionTag>::cast(isolate->factory()->NewStruct(
......
......@@ -41,6 +41,7 @@ class WasmCapiFunction;
class WasmDebugInfo;
class WasmExceptionTag;
class WasmExportedFunction;
class WasmExternalFunction;
class WasmInstanceObject;
class WasmJSFunction;
class WasmModuleObject;
......@@ -440,7 +441,7 @@ class WasmInstanceObject : public JSObject {
DECL_OPTIONAL_ACCESSORS(indirect_function_table_refs, FixedArray)
DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign)
DECL_OPTIONAL_ACCESSORS(exceptions_table, FixedArray)
DECL_OPTIONAL_ACCESSORS(wasm_exported_functions, FixedArray)
DECL_OPTIONAL_ACCESSORS(wasm_external_functions, FixedArray)
DECL_PRIMITIVE_ACCESSORS(memory_start, byte*)
DECL_PRIMITIVE_ACCESSORS(memory_size, size_t)
DECL_PRIMITIVE_ACCESSORS(memory_mask, size_t)
......@@ -499,7 +500,7 @@ class WasmInstanceObject : public JSObject {
V(kIndirectFunctionTablesOffset, kTaggedSize) \
V(kManagedNativeAllocationsOffset, kTaggedSize) \
V(kExceptionsTableOffset, kTaggedSize) \
V(kWasmExportedFunctionsOffset, kTaggedSize) \
V(kWasmExternalFunctionsOffset, kTaggedSize) \
V(kRealStackLimitAddressOffset, kSystemPointerSize) \
V(kDataSegmentStartsOffset, kSystemPointerSize) \
V(kDataSegmentSizesOffset, kSystemPointerSize) \
......@@ -538,7 +539,7 @@ class WasmInstanceObject : public JSObject {
kIndirectFunctionTablesOffset,
kManagedNativeAllocationsOffset,
kExceptionsTableOffset,
kWasmExportedFunctionsOffset};
kWasmExternalFunctionsOffset};
V8_EXPORT_PRIVATE const wasm::WasmModule* module();
......@@ -581,22 +582,22 @@ class WasmInstanceObject : public JSObject {
// Iterates all fields in the object except the untagged fields.
class BodyDescriptor;
static MaybeHandle<WasmExportedFunction> GetWasmExportedFunction(
static MaybeHandle<WasmExternalFunction> GetWasmExternalFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index);
// Acquires the {WasmExportedFunction} for a given {function_index} from the
// Acquires the {WasmExternalFunction} for a given {function_index} from the
// cache of the given {instance}, or creates a new {WasmExportedFunction} if
// it does not exist yet. The new {WasmExportedFunction} is added to the
// cache of the {instance} immediately.
V8_EXPORT_PRIVATE static Handle<WasmExportedFunction>
GetOrCreateWasmExportedFunction(Isolate* isolate,
V8_EXPORT_PRIVATE static Handle<WasmExternalFunction>
GetOrCreateWasmExternalFunction(Isolate* isolate,
Handle<WasmInstanceObject> instance,
int function_index);
static void SetWasmExportedFunction(Isolate* isolate,
static void SetWasmExternalFunction(Isolate* isolate,
Handle<WasmInstanceObject> instance,
int index,
Handle<WasmExportedFunction> val);
Handle<WasmExternalFunction> val);
// Imports a constructed {WasmJSFunction} into the indirect function table of
// this instance. Note that this might trigger wrapper compilation, since a
......@@ -719,6 +720,19 @@ class WasmCapiFunction : public JSFunction {
OBJECT_CONSTRUCTORS(WasmCapiFunction, JSFunction);
};
// Any external function that can be imported/exported in modules. This abstract
// class just dispatches to the following concrete classes:
// - {WasmExportedFunction}: A proper Wasm function exported from a module.
// - {WasmJSFunction}: A function constructed via WebAssembly.Function in JS.
// // TODO(wasm): Potentially {WasmCapiFunction} will be added here as well.
class WasmExternalFunction : public JSFunction {
public:
static bool IsWasmExternalFunction(Object object);
DECL_CAST(WasmExternalFunction)
OBJECT_CONSTRUCTORS(WasmExternalFunction, JSFunction);
};
class WasmIndirectFunctionTable : public Struct {
public:
DECL_PRIMITIVE_ACCESSORS(size, uint32_t)
......
......@@ -159,7 +159,7 @@ void TestingModuleBuilder::FreezeSignatureMapAndInitializeWrapperCache() {
Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
FreezeSignatureMapAndInitializeWrapperCache();
SetExecutable();
return WasmInstanceObject::GetOrCreateWasmExportedFunction(
return WasmInstanceObject::GetOrCreateWasmExternalFunction(
isolate_, instance_object(), index);
}
......
......@@ -613,6 +613,5 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
builder.addExport("fun2", fun_index);
let instance = builder.instantiate({ m: { fun: fun }});
assertSame(instance.exports.fun1, instance.exports.fun2);
// TODO(7742): Fix after https://github.com/WebAssembly/js-types/issues/11.
assertNotSame(fun, instance.exports.fun1);
assertSame(fun, instance.exports.fun1);
})();
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