blob: 4b8ee187e6914430996e8cb8b8e4e839427b9485 [file] [edit]
/*
* Copyright 2026 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ir/effects.h"
#include "matchers/effects.h"
#include "parser/wat-parser.h"
#include "wasm.h"
using namespace wasm;
using namespace testing;
namespace {
class EffectAnalyzerTest : public Test {
protected:
Module wasm;
PassOptions options;
void SetUp() override { wasm.features = FeatureSet::All; }
};
TEST_F(EffectAnalyzerTest, Suspend) {
auto moduleText = R"wasm(
(module
(tag $tag)
(func $test
(suspend $tag)
)
)
)wasm";
auto parseResult = WATParser::parseModule(wasm, moduleText);
ASSERT_FALSE(parseResult.getErr());
auto* func = wasm.getFunction("test");
ASSERT_NE(func, nullptr);
EffectAnalyzer effects(options, wasm, func->body);
// Suspension detection
EXPECT_TRUE(effects.suspends);
EXPECT_THAT(&effects, Suspends());
EXPECT_TRUE(effects.getSideEffects() & EffectAnalyzer::SideEffects::Suspends);
// Suspending executes arbitrary other code in the handler before resuming,
// modeled as a call.
EXPECT_TRUE(effects.calls);
EXPECT_THAT(&effects, Calls());
// Accesses all global mutable state via calls
EXPECT_TRUE(effects.accessesMemory());
EXPECT_TRUE(effects.accessesSharedMemory());
EXPECT_TRUE(effects.accessesTable());
EXPECT_TRUE(effects.accessesMutableStruct());
EXPECT_TRUE(effects.accessesSharedMutableStruct());
EXPECT_TRUE(effects.accessesArray());
EXPECT_TRUE(effects.accessesSharedArray());
EXPECT_TRUE(effects.writesGlobalState());
EXPECT_TRUE(effects.readsMutableGlobalState());
EXPECT_TRUE(effects.accessesSharedGlobalState());
// Control flow & side effect queries
EXPECT_TRUE(effects.transfersControlFlow());
EXPECT_TRUE(effects.hasNonTrapSideEffects());
EXPECT_TRUE(effects.hasSideEffects());
EXPECT_TRUE(effects.hasUnremovableSideEffects());
}
TEST_F(EffectAnalyzerTest, UnknownCall) {
auto moduleText = R"wasm(
(module
(func $callee)
(func $caller
(call $callee)
)
)
)wasm";
auto parseResult = WATParser::parseModule(wasm, moduleText);
ASSERT_FALSE(parseResult.getErr());
auto* caller = wasm.getFunction("caller");
ASSERT_NE(caller, nullptr);
// With stack switching enabled, unknown calls conservatively assume
// suspension.
wasm.features.setStackSwitching(true);
EffectAnalyzer effectsWithStackSwitch(options, wasm, caller->body);
EXPECT_TRUE(effectsWithStackSwitch.suspends);
// With stack switching disabled, calls do not suspend.
wasm.features.setStackSwitching(false);
EffectAnalyzer effectsWithoutStackSwitch(options, wasm, caller->body);
EXPECT_FALSE(effectsWithoutStackSwitch.suspends);
}
} // anonymous namespace