<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/simple_tensor.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include <vespa/eval/eval/simple_value.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/dense/dense_replace_type_function.h>
#include <vespa/eval/tensor/dense/dense_cell_range_function.h>
#include <vespa/eval/tensor/dense/dense_lambda_peek_function.h>
#include <vespa/eval/tensor/dense/dense_lambda_function.h>
#include <vespa/eval/tensor/dense/dense_fast_rename_optimizer.h>
#include <vespa/eval/tensor/dense/dense_tensor.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/tensor_nodes.h>

#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/stash.h>

using namespace vespalib;
using namespace vespalib::eval;
using namespace vespalib::eval::test;
using namespace vespalib::tensor;
using namespace vespalib::eval::tensor_function;

using EvalMode = DenseLambdaFunction::EvalMode;

namespace vespalib::tensor {

std::ostream &operator<<(std::ostream &os, EvalMode eval_mode)
{
    switch(eval_mode) {
    case EvalMode::COMPILED:    return os << "COMPILED";
    case EvalMode::INTERPRETED: return os << "INTERPRETED";
    }
    abort();
}

}

const TensorEngine &prod_engine = DefaultTensorEngine::ref();
const ValueBuilderFactory &simple_factory = SimpleValueBuilderFactory::get();

EvalFixture::ParamRepo make_params() {
    return EvalFixture::ParamRepo()
        .add("a", spec(1))
        .add("b", spec(2))
        .add("x3", spec({x(3)}, N()))
        .add("x3f", spec(float_cells({x(3)}), N()))
        .add("x3m", spec({x({"0", "1", "2"})}, N()))
        .add("x3y5", spec({x(3), y(5)}, N()))
        .add("x3y5f", spec(float_cells({x(3), y(5)}), N()))
        .add("x15", spec({x(15)}, N()))
        .add("x15f", spec(float_cells({x(15)}), N()));
}
EvalFixture::ParamRepo param_repo = make_params();

template <typename T, typename F>
void verify_impl(const vespalib::string &expr, const vespalib::string &expect, F &&inspect) {
    EvalFixture fixture(prod_engine, expr, param_repo, true);
    EvalFixture slow_fixture(prod_engine, expr, param_repo, false);
    EvalFixture simple_factory_fixture(simple_factory, expr, param_repo, false);
    EXPECT_EQUAL(fixture.result(), slow_fixture.result());
    EXPECT_EQUAL(fixture.result(), simple_factory_fixture.result());
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expect, param_repo));
    auto info = fixture.find_all<T>();
    if (EXPECT_EQUAL(info.size(), 1u)) {
        inspect(info[0]);
    }
}
template <typename T>
void verify_impl(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<T>(expr, expect, [](const T*){});
}

void verify_generic(const vespalib::string &expr, const vespalib::string &expect,
                    EvalMode expect_eval_mode)
{
    verify_impl<DenseLambdaFunction>(expr, expect,
                                     [&](const DenseLambdaFunction *info)
                                     {
                                         EXPECT_EQUAL(info->eval_mode(), expect_eval_mode);
                                     });
}

void verify_reshape(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<DenseReplaceTypeFunction>(expr, expect);
}

void verify_range(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<DenseCellRangeFunction>(expr, expect);
}

void verify_idx_fun(const vespalib::string &expr, const vespalib::string &expect,
                    const vespalib::string &expect_idx_fun)
{
    verify_impl<DenseLambdaPeekFunction>(expr, expect,
                                         [&](const DenseLambdaPeekFunction *info)
                                         {
                                             EXPECT_EQUAL(info->idx_fun_dump(), expect_idx_fun);
                                         });
}

void verify_const(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<ConstValue>(expr, expect);
}

//-----------------------------------------------------------------------------

TEST("require that simple constant tensor lambda works") {
    TEST_DO(verify_const("tensor(x[3])(x+1)", "tensor(x[3]):[1,2,3]"));
}

TEST("require that simple dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3])(x+a)", "tensor(x[3]):[1,2,3]", EvalMode::COMPILED));
}

TEST("require that compiled multi-dimensional multi-param dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3],y[2])((b-a)+x+y)", "tensor(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::COMPILED));
    TEST_DO(verify_generic("tensor<float>(x[3],y[2])((b-a)+x+y)", "tensor<float>(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::COMPILED));
}

TEST("require that interpreted multi-dimensional multi-param dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3],y[2])((x3{x:(a)}-a)+x+y)", "tensor(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor<float>(x[3],y[2])((x3{x:(a)}-a)+x+y)", "tensor<float>(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::INTERPRETED));
}

TEST("require that tensor lambda can be used for tensor slicing") {
    TEST_DO(verify_generic("tensor(x[2])(x3{x:(x+a)})", "tensor(x[2]):[2,3]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[2])(a+x3{x:(x)})", "tensor(x[2]):[2,3]", EvalMode::INTERPRETED));
}

TEST("require that tensor lambda can be used for cell type casting") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x3f{x:(x)})", "tensor(x[3]):[1,2,3]", "f(x)(x)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3{x:(x)})", "tensor<float>(x[3]):[1,2,3]", "f(x)(x)"));
}

TEST("require that tensor lambda can be used to convert from sparse to dense tensors") {
    TEST_DO(verify_generic("tensor(x[3])(x3m{x:(x)})", "tensor(x[3]):[1,2,3]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[2])(x3m{x:(x)})", "tensor(x[2]):[1,2]", EvalMode::INTERPRETED));
}

TEST("require that constant nested tensor lambda using tensor peek works") {
    TEST_DO(verify_const("tensor(x[2])(tensor(y[2])((x+y)+1){y:(x)})", "tensor(x[2]):[1,3]"));
}

TEST("require that dynamic nested tensor lambda using tensor peek works") {
    TEST_DO(verify_generic("tensor(x[2])(tensor(y[2])((x+y)+a){y:(x)})", "tensor(x[2]):[1,3]", EvalMode::INTERPRETED));
}

TEST("require that tensor reshape is optimized") {
    TEST_DO(verify_reshape("tensor(x[15])(x3y5{x:(x/5),y:(x%5)})", "x15"));
    TEST_DO(verify_reshape("tensor(x[3],y[5])(x15{x:(x*5+y)})", "x3y5"));
    TEST_DO(verify_reshape("tensor<float>(x[15])(x3y5f{x:(x/5),y:(x%5)})", "x15f"));
}

TEST("require that tensor reshape with non-matching cell type requires cell copy") {
    TEST_DO(verify_idx_fun("tensor(x[15])(x3y5f{x:(x/5),y:(x%5)})", "x15", "f(x)((floor((x/5))*5)+(x%5))"));
    TEST_DO(verify_idx_fun("tensor<float>(x[15])(x3y5{x:(x/5),y:(x%5)})", "x15f", "f(x)((floor((x/5))*5)+(x%5))"));
    TEST_DO(verify_idx_fun("tensor(x[3],y[5])(x15f{x:(x*5+y)})", "x3y5", "f(x,y)((x*5)+y)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3],y[5])(x15{x:(x*5+y)})", "x3y5f", "f(x,y)((x*5)+y)"));
}

TEST("require that tensor cell subrange view is optimized") {
    TEST_DO(verify_range("tensor(y[5])(x3y5{x:1,y:(y)})", "x3y5{x:1}"));
    TEST_DO(verify_range("tensor(x[3])(x15{x:(x+5)})", "tensor(x[3]):[6,7,8]"));
    TEST_DO(verify_range("tensor<float>(y[5])(x3y5f{x:1,y:(y)})", "x3y5f{x:1}"));
    TEST_DO(verify_range("tensor<float>(x[3])(x15f{x:(x+5)})", "tensor<float>(x[3]):[6,7,8]"));
}

TEST("require that tensor cell subrange with non-matching cell type requires cell copy") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x15f{x:(x+5)})", "tensor(x[3]):[6,7,8]", "f(x)(x+5)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x15{x:(x+5)})", "tensor<float>(x[3]):[6,7,8]", "f(x)(x+5)"));
}

TEST("require that non-continuous cell extraction is optimized") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x3y5{x:(x),y:2})", "x3y5{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor(x[3])(x3y5f{x:(x),y:2})", "x3y5{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3y5{x:(x),y:2})", "x3y5f{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3y5f{x:(x),y:2})", "x3y5f{y:2}", "f(x)((floor(x)*5)+2)"));
}

TEST("require that out-of-bounds cell extraction is not optimized") {
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:1,y:(x+3)})", "tensor(x[3]):[9,10,0]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:1,y:(x-1)})", "tensor(x[3]):[0,6,7]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:(x+1),y:(x)})", "tensor(x[3]):[6,12,0]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:(x-1),y:(x)})", "tensor(x[3]):[0,2,8]", EvalMode::INTERPRETED));
}

TEST("require that non-double result from inner tensor lambda function fails type resolving") {
    auto fun_a = Function::parse("tensor(x[2])(a)");
    auto fun_b = Function::parse("tensor(x[2])(a{y:(x)})");
    NodeTypes types_ad(*fun_a, {ValueType::from_spec("double")});
    NodeTypes types_at(*fun_a, {ValueType::from_spec("tensor(y[2])")});
    NodeTypes types_bd(*fun_b, {ValueType::from_spec("double")});
    NodeTypes types_bt(*fun_b, {ValueType::from_spec("tensor(y[2])")});
    EXPECT_EQUAL(types_ad.get_type(fun_a->root()).to_spec(), "tensor(x[2])");
    EXPECT_EQUAL(types_at.get_type(fun_a->root()).to_spec(), "error");
    EXPECT_EQUAL(types_bd.get_type(fun_b->root()).to_spec(), "error");
    EXPECT_EQUAL(types_bt.get_type(fun_b->root()).to_spec(), "tensor(x[2])");
}

TEST("require that type resolving also include nodes in the inner tensor lambda function") {
    auto fun = Function::parse("tensor(x[2])(a)");
    NodeTypes types(*fun, {ValueType::from_spec("double")});
    auto lambda = nodes::as<nodes::TensorLambda>(fun->root());
    ASSERT_TRUE(lambda != nullptr);
    EXPECT_EQUAL(types.get_type(*lambda).to_spec(), "tensor(x[2])");
    auto symbol = nodes::as<nodes::Symbol>(lambda->lambda().root());
    ASSERT_TRUE(symbol != nullptr);
    EXPECT_EQUAL(types.get_type(*symbol).to_spec(), "double");
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>also add test of type exporting for inner lambdas<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/simple_tensor.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include <vespa/eval/eval/simple_value.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/dense/dense_replace_type_function.h>
#include <vespa/eval/tensor/dense/dense_cell_range_function.h>
#include <vespa/eval/tensor/dense/dense_lambda_peek_function.h>
#include <vespa/eval/tensor/dense/dense_lambda_function.h>
#include <vespa/eval/tensor/dense/dense_fast_rename_optimizer.h>
#include <vespa/eval/tensor/dense/dense_tensor.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/tensor_nodes.h>

#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/stash.h>

using namespace vespalib;
using namespace vespalib::eval;
using namespace vespalib::eval::test;
using namespace vespalib::tensor;
using namespace vespalib::eval::tensor_function;

using EvalMode = DenseLambdaFunction::EvalMode;

namespace vespalib::tensor {

std::ostream &operator<<(std::ostream &os, EvalMode eval_mode)
{
    switch(eval_mode) {
    case EvalMode::COMPILED:    return os << "COMPILED";
    case EvalMode::INTERPRETED: return os << "INTERPRETED";
    }
    abort();
}

}

const TensorEngine &prod_engine = DefaultTensorEngine::ref();
const ValueBuilderFactory &simple_factory = SimpleValueBuilderFactory::get();

EvalFixture::ParamRepo make_params() {
    return EvalFixture::ParamRepo()
        .add("a", spec(1))
        .add("b", spec(2))
        .add("x3", spec({x(3)}, N()))
        .add("x3f", spec(float_cells({x(3)}), N()))
        .add("x3m", spec({x({"0", "1", "2"})}, N()))
        .add("x3y5", spec({x(3), y(5)}, N()))
        .add("x3y5f", spec(float_cells({x(3), y(5)}), N()))
        .add("x15", spec({x(15)}, N()))
        .add("x15f", spec(float_cells({x(15)}), N()));
}
EvalFixture::ParamRepo param_repo = make_params();

template <typename T, typename F>
void verify_impl(const vespalib::string &expr, const vespalib::string &expect, F &&inspect) {
    EvalFixture fixture(prod_engine, expr, param_repo, true);
    EvalFixture slow_fixture(prod_engine, expr, param_repo, false);
    EvalFixture simple_factory_fixture(simple_factory, expr, param_repo, false);
    EXPECT_EQUAL(fixture.result(), slow_fixture.result());
    EXPECT_EQUAL(fixture.result(), simple_factory_fixture.result());
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expect, param_repo));
    auto info = fixture.find_all<T>();
    if (EXPECT_EQUAL(info.size(), 1u)) {
        inspect(info[0]);
    }
}
template <typename T>
void verify_impl(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<T>(expr, expect, [](const T*){});
}

void verify_generic(const vespalib::string &expr, const vespalib::string &expect,
                    EvalMode expect_eval_mode)
{
    verify_impl<DenseLambdaFunction>(expr, expect,
                                     [&](const DenseLambdaFunction *info)
                                     {
                                         EXPECT_EQUAL(info->eval_mode(), expect_eval_mode);
                                     });
}

void verify_reshape(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<DenseReplaceTypeFunction>(expr, expect);
}

void verify_range(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<DenseCellRangeFunction>(expr, expect);
}

void verify_idx_fun(const vespalib::string &expr, const vespalib::string &expect,
                    const vespalib::string &expect_idx_fun)
{
    verify_impl<DenseLambdaPeekFunction>(expr, expect,
                                         [&](const DenseLambdaPeekFunction *info)
                                         {
                                             EXPECT_EQUAL(info->idx_fun_dump(), expect_idx_fun);
                                         });
}

void verify_const(const vespalib::string &expr, const vespalib::string &expect) {
    verify_impl<ConstValue>(expr, expect);
}

//-----------------------------------------------------------------------------

TEST("require that simple constant tensor lambda works") {
    TEST_DO(verify_const("tensor(x[3])(x+1)", "tensor(x[3]):[1,2,3]"));
}

TEST("require that simple dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3])(x+a)", "tensor(x[3]):[1,2,3]", EvalMode::COMPILED));
}

TEST("require that compiled multi-dimensional multi-param dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3],y[2])((b-a)+x+y)", "tensor(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::COMPILED));
    TEST_DO(verify_generic("tensor<float>(x[3],y[2])((b-a)+x+y)", "tensor<float>(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::COMPILED));
}

TEST("require that interpreted multi-dimensional multi-param dynamic tensor lambda works") {
    TEST_DO(verify_generic("tensor(x[3],y[2])((x3{x:(a)}-a)+x+y)", "tensor(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor<float>(x[3],y[2])((x3{x:(a)}-a)+x+y)", "tensor<float>(x[3],y[2]):[[1,2],[2,3],[3,4]]", EvalMode::INTERPRETED));
}

TEST("require that tensor lambda can be used for tensor slicing") {
    TEST_DO(verify_generic("tensor(x[2])(x3{x:(x+a)})", "tensor(x[2]):[2,3]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[2])(a+x3{x:(x)})", "tensor(x[2]):[2,3]", EvalMode::INTERPRETED));
}

TEST("require that tensor lambda can be used for cell type casting") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x3f{x:(x)})", "tensor(x[3]):[1,2,3]", "f(x)(x)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3{x:(x)})", "tensor<float>(x[3]):[1,2,3]", "f(x)(x)"));
}

TEST("require that tensor lambda can be used to convert from sparse to dense tensors") {
    TEST_DO(verify_generic("tensor(x[3])(x3m{x:(x)})", "tensor(x[3]):[1,2,3]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[2])(x3m{x:(x)})", "tensor(x[2]):[1,2]", EvalMode::INTERPRETED));
}

TEST("require that constant nested tensor lambda using tensor peek works") {
    TEST_DO(verify_const("tensor(x[2])(tensor(y[2])((x+y)+1){y:(x)})", "tensor(x[2]):[1,3]"));
}

TEST("require that dynamic nested tensor lambda using tensor peek works") {
    TEST_DO(verify_generic("tensor(x[2])(tensor(y[2])((x+y)+a){y:(x)})", "tensor(x[2]):[1,3]", EvalMode::INTERPRETED));
}

TEST("require that tensor reshape is optimized") {
    TEST_DO(verify_reshape("tensor(x[15])(x3y5{x:(x/5),y:(x%5)})", "x15"));
    TEST_DO(verify_reshape("tensor(x[3],y[5])(x15{x:(x*5+y)})", "x3y5"));
    TEST_DO(verify_reshape("tensor<float>(x[15])(x3y5f{x:(x/5),y:(x%5)})", "x15f"));
}

TEST("require that tensor reshape with non-matching cell type requires cell copy") {
    TEST_DO(verify_idx_fun("tensor(x[15])(x3y5f{x:(x/5),y:(x%5)})", "x15", "f(x)((floor((x/5))*5)+(x%5))"));
    TEST_DO(verify_idx_fun("tensor<float>(x[15])(x3y5{x:(x/5),y:(x%5)})", "x15f", "f(x)((floor((x/5))*5)+(x%5))"));
    TEST_DO(verify_idx_fun("tensor(x[3],y[5])(x15f{x:(x*5+y)})", "x3y5", "f(x,y)((x*5)+y)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3],y[5])(x15{x:(x*5+y)})", "x3y5f", "f(x,y)((x*5)+y)"));
}

TEST("require that tensor cell subrange view is optimized") {
    TEST_DO(verify_range("tensor(y[5])(x3y5{x:1,y:(y)})", "x3y5{x:1}"));
    TEST_DO(verify_range("tensor(x[3])(x15{x:(x+5)})", "tensor(x[3]):[6,7,8]"));
    TEST_DO(verify_range("tensor<float>(y[5])(x3y5f{x:1,y:(y)})", "x3y5f{x:1}"));
    TEST_DO(verify_range("tensor<float>(x[3])(x15f{x:(x+5)})", "tensor<float>(x[3]):[6,7,8]"));
}

TEST("require that tensor cell subrange with non-matching cell type requires cell copy") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x15f{x:(x+5)})", "tensor(x[3]):[6,7,8]", "f(x)(x+5)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x15{x:(x+5)})", "tensor<float>(x[3]):[6,7,8]", "f(x)(x+5)"));
}

TEST("require that non-continuous cell extraction is optimized") {
    TEST_DO(verify_idx_fun("tensor(x[3])(x3y5{x:(x),y:2})", "x3y5{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor(x[3])(x3y5f{x:(x),y:2})", "x3y5{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3y5{x:(x),y:2})", "x3y5f{y:2}", "f(x)((floor(x)*5)+2)"));
    TEST_DO(verify_idx_fun("tensor<float>(x[3])(x3y5f{x:(x),y:2})", "x3y5f{y:2}", "f(x)((floor(x)*5)+2)"));
}

TEST("require that out-of-bounds cell extraction is not optimized") {
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:1,y:(x+3)})", "tensor(x[3]):[9,10,0]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:1,y:(x-1)})", "tensor(x[3]):[0,6,7]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:(x+1),y:(x)})", "tensor(x[3]):[6,12,0]", EvalMode::INTERPRETED));
    TEST_DO(verify_generic("tensor(x[3])(x3y5{x:(x-1),y:(x)})", "tensor(x[3]):[0,2,8]", EvalMode::INTERPRETED));
}

TEST("require that non-double result from inner tensor lambda function fails type resolving") {
    auto fun_a = Function::parse("tensor(x[2])(a)");
    auto fun_b = Function::parse("tensor(x[2])(a{y:(x)})");
    NodeTypes types_ad(*fun_a, {ValueType::from_spec("double")});
    NodeTypes types_at(*fun_a, {ValueType::from_spec("tensor(y[2])")});
    NodeTypes types_bd(*fun_b, {ValueType::from_spec("double")});
    NodeTypes types_bt(*fun_b, {ValueType::from_spec("tensor(y[2])")});
    EXPECT_EQUAL(types_ad.get_type(fun_a->root()).to_spec(), "tensor(x[2])");
    EXPECT_EQUAL(types_at.get_type(fun_a->root()).to_spec(), "error");
    EXPECT_EQUAL(types_bd.get_type(fun_b->root()).to_spec(), "error");
    EXPECT_EQUAL(types_bt.get_type(fun_b->root()).to_spec(), "tensor(x[2])");
}

TEST("require that type resolving also include nodes in the inner tensor lambda function") {
    auto fun = Function::parse("tensor(x[2])(a)");
    NodeTypes types(*fun, {ValueType::from_spec("double")});
    auto lambda = nodes::as<nodes::TensorLambda>(fun->root());
    ASSERT_TRUE(lambda != nullptr);
    EXPECT_EQUAL(types.get_type(*lambda).to_spec(), "tensor(x[2])");
    auto symbol = nodes::as<nodes::Symbol>(lambda->lambda().root());
    ASSERT_TRUE(symbol != nullptr);
    EXPECT_EQUAL(types.get_type(*symbol).to_spec(), "double");
}

TEST("require that type exporting also include nodes in the inner tensor lambda function") {
    auto fun = Function::parse("tensor(x[2])(tensor(y[2])((x+y)+a){y:(x)})");
    NodeTypes types(*fun, {ValueType::from_spec("double")});
    const auto &root = fun->root();
    auto lambda = nodes::as<nodes::TensorLambda>(root);
    ASSERT_TRUE(lambda != nullptr);
    NodeTypes outer = types.export_types(root);
    ASSERT_TRUE(outer.errors().empty());
    NodeTypes inner = outer.export_types(lambda->lambda().root());
    EXPECT_TRUE(inner.errors().empty());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before><commit_msg>Another --enable-complex fix, plus remove some atavistic code.<commit_after><|endoftext|>


<commit_before>//===--- IRGenSIL.cpp - Swift Per-Function IR Generation ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements basic setup and teardown for the class which
//  performs IR generation for function bodies.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/SourceMgr.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"

#include "CallEmission.h"
#include "Explosion.h"
#include "GenFunc.h"
#include "GenMeta.h"
#include "GenTuple.h"
#include "IRGenModule.h"
#include "IRGenSIL.h"
#include "Linking.h"

using namespace swift;
using namespace irgen;

IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM,
                                   CanType t,
                                   ArrayRef<Pattern*> p,
                                   llvm::Function *fn)
  : IRGenFunction(IGM, t, p, ExplosionKind::Minimal, 0, fn, Prologue::None)
{
}

IRGenSILFunction::~IRGenSILFunction() {
}

void IRGenSILFunction::emitSILFunction(swift::Function *f) {
  assert(!f->empty() && "function has no basic blocks?!");

  BasicBlock *entry = f->begin();

  // FIXME Map the SIL arguments to LLVM arguments.
  size_t bbarg_size = entry->bbarg_end() - entry->bbarg_begin();
  assert(bbarg_size == 0 && "arguments not yet supported");

  // Emit the function body.
  visitBasicBlock(entry);
}

void IRGenSILFunction::emitGlobalTopLevel(TranslationUnit *TU,
                                          SILModule *SILMod) {
  // Emit the toplevel function.
  if (SILMod->hasTopLevelFunction())
    emitSILFunction(SILMod->getTopLevelFunction());

  // FIXME: should support nonzero StartElems for interactive contexts.
  IRGenFunction::emitGlobalTopLevel(TU, 0);
}

void IRGenSILFunction::visitBasicBlock(swift::BasicBlock *BB) {
  // Emit the LLVM basic block.
  // FIXME: Use the SIL basic block's name.
  llvm::BasicBlock *curBB = llvm::BasicBlock::Create(IGM.getLLVMContext(),
                                                    "sil");
  CurFn->getBasicBlockList().push_back(curBB);
  Builder.SetInsertPoint(curBB);

  // FIXME: emit a phi node to bind the bb arguments from all the predecessor
  // branches.

  // Generate the body.
  for (auto &I : *BB)
    visit(&I);

  assert(Builder.hasPostTerminatorIP() && "SIL bb did not terminate block?!");
}

void IRGenSILFunction::visitTupleInst(swift::TupleInst *i) {
  // FIXME SILGen emits empty tuple instructions in the 'hello world' example
  // but doesn't use them for anything. Cheat here and ignore tuple insts.
  assert(i->getElements().empty() &&
         "non-empty tuples not implemented "
         "(and neither are empty tuples, really)");
}

void IRGenSILFunction::visitConstantRefInst(swift::ConstantRefInst *i) {
  // Find the entry point corresponding to the SILConstant.
  // FIXME: currently only does uncurried FuncDecls
  SILConstant constant = i->getConstant();
  assert(constant.id == 0 &&
         "constant_ref alternate entry points not yet handled");
  ValueDecl *vd = constant.loc.dyn_cast<ValueDecl*>();
  FuncDecl *fd = cast<FuncDecl>(vd);

  unsigned naturalCurryLevel = getDeclNaturalUncurryLevel(fd);

  FunctionRef fnRef(fd, ExplosionKind::Minimal, naturalCurryLevel);
  llvm::Value *fnptr = IGM.getAddrOfFunction(fnRef, ExtraData::None);

  AbstractCC cc = fd->isInstanceMember()
    ? AbstractCC::Method
    : AbstractCC::Freestanding;

  // Prepare a CallEmission for this function.
  // FIXME generic call specialization
  CanType origType = i->getType().getSwiftType();
  CanType resultType = getResultType(origType, naturalCurryLevel);

  Callee callee = Callee::forKnownFunction(
                               cc,
                               origType,
                               resultType,
                               /*Substitutions=*/ {},
                               fnptr,
                               ManagedValue(),
                               ExplosionKind::Minimal,
                               naturalCurryLevel);
  newLoweredPartialCall(Value(i, 0),
                        naturalCurryLevel,
                        CallEmission(*this, callee));
}

void IRGenSILFunction::visitMetatypeInst(swift::MetatypeInst *i) {
  emitMetaTypeRef(*this, i->getType().getSwiftType(),
                  newLoweredExplosion(Value(i, 0)));
}

void IRGenSILFunction::visitApplyInst(swift::ApplyInst *i) {
  Value v(i, 0);

  // FIXME: This assumes that a curried application always reaches the "natural"
  // curry level and that intermediate curries are never used as values. These
  // conditions aren't supported for many decls in IRGen anyway though.

  // Pile our arguments onto the CallEmission.
  PartialCall &parent = getLoweredPartialCall(i->getCallee());
  Explosion args(ExplosionKind::Minimal);
  for (Value arg : i->getArguments()) {
    args.add(getLoweredExplosion(arg).getAll());
  }
  parent.emission.addArg(args);

  if (--parent.remainingCurryLevels == 0) {
    // If this brought the call to its natural curry level, emit the call.
    parent.emission.emitToExplosion(newLoweredExplosion(v));
  } else {
    // If not, pass the partial emission forward.
    moveLoweredPartialCall(v, std::move(parent));
  }
}

void IRGenSILFunction::visitStringLiteralInst(swift::StringLiteralInst *i) {
  emitStringLiteral(*this, i->getValue(), /*includeSize=*/true,
                    newLoweredExplosion(Value(i, 0)));
}

void IRGenSILFunction::visitExtractInst(swift::ExtractInst *i) {
  Value v(i, 0);
  Explosion &lowered = newLoweredExplosion(v);
  Explosion &operand = getLoweredExplosion(i->getOperand());
  // FIXME: totally wrong for nontrivial cases. should get the actual range
  // for the extracted element
  ArrayRef<ManagedValue> extracted = operand.getRange(i->getFieldNo(),
                                                     i->getFieldNo() + 1);

  lowered.add(extracted);
}

void IRGenSILFunction::visitReturnInst(swift::ReturnInst *i) {
  // FIXME: actually return a value.
  Builder.CreateRet(nullptr);
}<commit_msg>Fix a build warning (missing newline at EOF)<commit_after>//===--- IRGenSIL.cpp - Swift Per-Function IR Generation ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements basic setup and teardown for the class which
//  performs IR generation for function bodies.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/SourceMgr.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"

#include "CallEmission.h"
#include "Explosion.h"
#include "GenFunc.h"
#include "GenMeta.h"
#include "GenTuple.h"
#include "IRGenModule.h"
#include "IRGenSIL.h"
#include "Linking.h"

using namespace swift;
using namespace irgen;

IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM,
                                   CanType t,
                                   ArrayRef<Pattern*> p,
                                   llvm::Function *fn)
  : IRGenFunction(IGM, t, p, ExplosionKind::Minimal, 0, fn, Prologue::None)
{
}

IRGenSILFunction::~IRGenSILFunction() {
}

void IRGenSILFunction::emitSILFunction(swift::Function *f) {
  assert(!f->empty() && "function has no basic blocks?!");

  BasicBlock *entry = f->begin();

  // FIXME Map the SIL arguments to LLVM arguments.
  size_t bbarg_size = entry->bbarg_end() - entry->bbarg_begin();
  assert(bbarg_size == 0 && "arguments not yet supported");

  // Emit the function body.
  visitBasicBlock(entry);
}

void IRGenSILFunction::emitGlobalTopLevel(TranslationUnit *TU,
                                          SILModule *SILMod) {
  // Emit the toplevel function.
  if (SILMod->hasTopLevelFunction())
    emitSILFunction(SILMod->getTopLevelFunction());

  // FIXME: should support nonzero StartElems for interactive contexts.
  IRGenFunction::emitGlobalTopLevel(TU, 0);
}

void IRGenSILFunction::visitBasicBlock(swift::BasicBlock *BB) {
  // Emit the LLVM basic block.
  // FIXME: Use the SIL basic block's name.
  llvm::BasicBlock *curBB = llvm::BasicBlock::Create(IGM.getLLVMContext(),
                                                    "sil");
  CurFn->getBasicBlockList().push_back(curBB);
  Builder.SetInsertPoint(curBB);

  // FIXME: emit a phi node to bind the bb arguments from all the predecessor
  // branches.

  // Generate the body.
  for (auto &I : *BB)
    visit(&I);

  assert(Builder.hasPostTerminatorIP() && "SIL bb did not terminate block?!");
}

void IRGenSILFunction::visitTupleInst(swift::TupleInst *i) {
  // FIXME SILGen emits empty tuple instructions in the 'hello world' example
  // but doesn't use them for anything. Cheat here and ignore tuple insts.
  assert(i->getElements().empty() &&
         "non-empty tuples not implemented "
         "(and neither are empty tuples, really)");
}

void IRGenSILFunction::visitConstantRefInst(swift::ConstantRefInst *i) {
  // Find the entry point corresponding to the SILConstant.
  // FIXME: currently only does uncurried FuncDecls
  SILConstant constant = i->getConstant();
  assert(constant.id == 0 &&
         "constant_ref alternate entry points not yet handled");
  ValueDecl *vd = constant.loc.dyn_cast<ValueDecl*>();
  FuncDecl *fd = cast<FuncDecl>(vd);

  unsigned naturalCurryLevel = getDeclNaturalUncurryLevel(fd);

  FunctionRef fnRef(fd, ExplosionKind::Minimal, naturalCurryLevel);
  llvm::Value *fnptr = IGM.getAddrOfFunction(fnRef, ExtraData::None);

  AbstractCC cc = fd->isInstanceMember()
    ? AbstractCC::Method
    : AbstractCC::Freestanding;

  // Prepare a CallEmission for this function.
  // FIXME generic call specialization
  CanType origType = i->getType().getSwiftType();
  CanType resultType = getResultType(origType, naturalCurryLevel);

  Callee callee = Callee::forKnownFunction(
                               cc,
                               origType,
                               resultType,
                               /*Substitutions=*/ {},
                               fnptr,
                               ManagedValue(),
                               ExplosionKind::Minimal,
                               naturalCurryLevel);
  newLoweredPartialCall(Value(i, 0),
                        naturalCurryLevel,
                        CallEmission(*this, callee));
}

void IRGenSILFunction::visitMetatypeInst(swift::MetatypeInst *i) {
  emitMetaTypeRef(*this, i->getType().getSwiftType(),
                  newLoweredExplosion(Value(i, 0)));
}

void IRGenSILFunction::visitApplyInst(swift::ApplyInst *i) {
  Value v(i, 0);

  // FIXME: This assumes that a curried application always reaches the "natural"
  // curry level and that intermediate curries are never used as values. These
  // conditions aren't supported for many decls in IRGen anyway though.

  // Pile our arguments onto the CallEmission.
  PartialCall &parent = getLoweredPartialCall(i->getCallee());
  Explosion args(ExplosionKind::Minimal);
  for (Value arg : i->getArguments()) {
    args.add(getLoweredExplosion(arg).getAll());
  }
  parent.emission.addArg(args);

  if (--parent.remainingCurryLevels == 0) {
    // If this brought the call to its natural curry level, emit the call.
    parent.emission.emitToExplosion(newLoweredExplosion(v));
  } else {
    // If not, pass the partial emission forward.
    moveLoweredPartialCall(v, std::move(parent));
  }
}

void IRGenSILFunction::visitStringLiteralInst(swift::StringLiteralInst *i) {
  emitStringLiteral(*this, i->getValue(), /*includeSize=*/true,
                    newLoweredExplosion(Value(i, 0)));
}

void IRGenSILFunction::visitExtractInst(swift::ExtractInst *i) {
  Value v(i, 0);
  Explosion &lowered = newLoweredExplosion(v);
  Explosion &operand = getLoweredExplosion(i->getOperand());
  // FIXME: totally wrong for nontrivial cases. should get the actual range
  // for the extracted element
  ArrayRef<ManagedValue> extracted = operand.getRange(i->getFieldNo(),
                                                     i->getFieldNo() + 1);

  lowered.add(extracted);
}

void IRGenSILFunction::visitReturnInst(swift::ReturnInst *i) {
  // FIXME: actually return a value.
  Builder.CreateRet(nullptr);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: splittbl.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2004-08-23 09:10:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifdef SW_DLLIMPLEMENTATION
#undef SW_DLLIMPLEMENTATION
#endif

#pragma hdrstop

#ifndef _WRTSH_HXX //autogen
#include <wrtsh.hxx>
#endif
#ifndef _SPLITTBL_HXX
#include <splittbl.hxx>
#endif
#include <splittbl.hrc>
#include <table.hrc>
#ifndef _TBLENUM_HXX
#include <tblenum.hxx>
#endif
/*-----------------17.03.98 10:56-------------------

--------------------------------------------------*/
SwSplitTblDlg::SwSplitTblDlg( Window *pParent, SwWrtShell &rSh ) :
    SvxStandardDialog(pParent, SW_RES(DLG_SPLIT_TABLE)),
    aOKPB(                  this, ResId(PB_OK       )),
    aCancelPB(              this, ResId(PB_CANCEL   )),
    aHelpPB(                this, ResId(PB_HELP     )),
    aSplitFL(               this, ResId(FL_SPLIT    )),
    aCntntCopyRB(           this, ResId(RB_CNTNT    )),
    aBoxAttrCopyWithParaRB( this, ResId(RB_BOX_PARA )),
    aBoxAttrCopyNoParaRB(   this, ResId(RB_BOX_NOPARA)),
    aBorderCopyRB(          this, ResId(RB_BORDER   )),
    rShell(rSh)
{
    FreeResource();
    aCntntCopyRB.Check();
}

/*-----------------17.03.98 10:56-------------------

--------------------------------------------------*/
void SwSplitTblDlg::Apply()
{
    USHORT nSplit = HEADLINE_CNTNTCOPY;
    if(aBoxAttrCopyWithParaRB.IsChecked())
        nSplit = HEADLINE_BOXATRCOLLCOPY;
    if(aBoxAttrCopyNoParaRB.IsChecked())
        nSplit = HEADLINE_BOXATTRCOPY;
    else if(aBorderCopyRB.IsChecked())
        nSplit = HEADLINE_BORDERCOPY;

    rShell.SplitTable( nSplit );

}

<commit_msg>INTEGRATION: CWS ooo19126 (1.5.596); FILE MERGED 2005/09/05 13:47:31 rt 1.5.596.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: splittbl.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 11:03:50 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifdef SW_DLLIMPLEMENTATION
#undef SW_DLLIMPLEMENTATION
#endif

#pragma hdrstop

#ifndef _WRTSH_HXX //autogen
#include <wrtsh.hxx>
#endif
#ifndef _SPLITTBL_HXX
#include <splittbl.hxx>
#endif
#include <splittbl.hrc>
#include <table.hrc>
#ifndef _TBLENUM_HXX
#include <tblenum.hxx>
#endif
/*-----------------17.03.98 10:56-------------------

--------------------------------------------------*/
SwSplitTblDlg::SwSplitTblDlg( Window *pParent, SwWrtShell &rSh ) :
    SvxStandardDialog(pParent, SW_RES(DLG_SPLIT_TABLE)),
    aOKPB(                  this, ResId(PB_OK       )),
    aCancelPB(              this, ResId(PB_CANCEL   )),
    aHelpPB(                this, ResId(PB_HELP     )),
    aSplitFL(               this, ResId(FL_SPLIT    )),
    aCntntCopyRB(           this, ResId(RB_CNTNT    )),
    aBoxAttrCopyWithParaRB( this, ResId(RB_BOX_PARA )),
    aBoxAttrCopyNoParaRB(   this, ResId(RB_BOX_NOPARA)),
    aBorderCopyRB(          this, ResId(RB_BORDER   )),
    rShell(rSh)
{
    FreeResource();
    aCntntCopyRB.Check();
}

/*-----------------17.03.98 10:56-------------------

--------------------------------------------------*/
void SwSplitTblDlg::Apply()
{
    USHORT nSplit = HEADLINE_CNTNTCOPY;
    if(aBoxAttrCopyWithParaRB.IsChecked())
        nSplit = HEADLINE_BOXATRCOLLCOPY;
    if(aBoxAttrCopyNoParaRB.IsChecked())
        nSplit = HEADLINE_BOXATTRCOPY;
    else if(aBorderCopyRB.IsChecked())
        nSplit = HEADLINE_BORDERCOPY;

    rShell.SplitTable( nSplit );

}

<|endoftext|>


<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/slobrok/server/local_rpc_monitor_map.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/fnet/scheduler.h>
#include <map>

using namespace vespalib;
using namespace slobrok;
using vespalib::make_string_short::fmt;

struct MapCall {
    vespalib::string name;
    ServiceMapping mapping;
    ServiceMapping old;
    static MapCall add(const ServiceMapping &m) { return {"add", m, {"",""}}; }
    static MapCall remove(const ServiceMapping &m) { return {"remove", m, {"",""}}; }
    static MapCall update(const ServiceMapping &o, const ServiceMapping &m) { return {"update", m, o}; }
    void check(const MapCall &rhs) const {
        EXPECT_EQ(name, rhs.name);
        EXPECT_EQ(mapping, rhs.mapping);
        EXPECT_EQ(old, rhs.old);
    }
    ~MapCall();
};
MapCall::~MapCall() = default;

struct MonitorCall {
    vespalib::string name;
    ServiceMapping mapping;
    bool hurry;
    static MonitorCall start(const ServiceMapping &m, bool h) { return {"start", m, h}; }
    static MonitorCall stop(const ServiceMapping &m) { return {"stop", m, false}; }
    void check(const MonitorCall &rhs) const {
        EXPECT_EQ(name, rhs.name);
        EXPECT_EQ(mapping, rhs.mapping);
        EXPECT_EQ(hurry, rhs.hurry);
    }
    ~MonitorCall();
};
MonitorCall::~MonitorCall() = default;

template <typename Call>
class CallLog {
private:
    std::vector<Call> _calls;
    size_t _checked;
public:
    CallLog() noexcept : _calls(), _checked(0) {}
    ~CallLog() { EXPECT_EQ(_calls.size(), _checked); }
    void log(Call call) { _calls.push_back(call); }
    void expect(std::initializer_list<Call> list) {
        ASSERT_EQ(list.size(), (_calls.size() - _checked));
        for (const auto &call: list) {
            call.check(_calls[_checked++]);
        }
    }
};

struct MapLog : CallLog<MapCall>, MapListener {
    void add(const ServiceMapping &mapping) override {
        log(MapCall::add(mapping));
    }
    void remove(const ServiceMapping &mapping) override {
        log(MapCall::remove(mapping));
    }
    void update(const ServiceMapping &old_mapping,
                const ServiceMapping &new_mapping) override
    {
        log(MapCall::update(old_mapping, new_mapping));
    }
};

struct MonitorLog : CallLog<MonitorCall>, MappingMonitor {
    void start(const ServiceMapping& mapping, bool hurry) override {
        log(MonitorCall::start(mapping, hurry));
    }
    void stop(const ServiceMapping& mapping) override {
        log(MonitorCall::stop(mapping));
    }
};

struct MyMappingMonitor : MappingMonitor {
    MonitorLog &monitor;
    MyMappingMonitor(MonitorLog &m) : monitor(m) {}
    void start(const ServiceMapping& mapping, bool hurry) override {
        monitor.start(mapping, hurry);
    }
    void stop(const ServiceMapping& mapping) override {
        monitor.stop(mapping);
    }
};

struct LocalRpcMonitorMapTest : public ::testing::Test {
    steady_time time;
    FNET_Scheduler scheduler;
    MonitorLog monitor_log;
    MapLog map_log;
    LocalRpcMonitorMap map;
    std::unique_ptr<MapSubscription> subscription;
    ServiceMapping mapping;
    ServiceMapping mapping_conflict;
    LocalRpcMonitorMapTest()
      : time(duration::zero()),
        scheduler(&time, &time), monitor_log(), map_log(),
        map(&scheduler, [this](auto &owner)
            {
                EXPECT_EQ(&owner, &map);
                return std::make_unique<MyMappingMonitor>(monitor_log);
            }),
        subscription(MapSubscription::subscribe(map.dispatcher(), map_log)),
        mapping("dummy_service", "dummy_spec"),
        mapping_conflict("dummy_service", "conflicting_dummy_spec")
    {}
    void tick(duration elapsed = FNET_Scheduler::tick_ms) {
        time += elapsed;
        scheduler.CheckTasks();
    }
    void add_mapping(const ServiceMapping &m, bool is_up) {
        map.add(m); // <- add from consensus map
        monitor_log.expect({});
        tick(0ms); // <- process delayed add event
        monitor_log.expect({MonitorCall::start(m, false)});
        map_log.expect({});
        if (is_up) {
            map.up(m); // <- up from monitor
            map_log.expect({MapCall::add(m)});
        } else {
            map.down(m); // <- down from monitor
            map_log.expect({});
        }
    }
    void flip_up_state(const ServiceMapping &m, bool was_up, size_t cnt) {
        for (size_t i = 0; i < cnt; ++i) {
            if (was_up) {
                map.up(m);
                map_log.expect({});
                map.down(m);
                map_log.expect({MapCall::remove(m)});
            } else {
                map.down(m);
                map_log.expect({});
                map.up(m);
                map_log.expect({MapCall::add(m)});
            }
            was_up = !was_up;
        }
        monitor_log.expect({});
    }
    void remove_mapping(const ServiceMapping &m, bool was_up) {
        map.remove(m); // <- remove from consensus map
        monitor_log.expect({});
        tick(0ms); // <- process delayed remove event
        monitor_log.expect({MonitorCall::stop(m)});
        if (was_up) {
            map_log.expect({MapCall::remove(m)});
        } else {
            map_log.expect({});
        }
    }
    ~LocalRpcMonitorMapTest();
};
LocalRpcMonitorMapTest::~LocalRpcMonitorMapTest() = default;

struct MyAddLocalHandler : LocalRpcMonitorMap::AddLocalCompletionHandler {
    std::unique_ptr<OkState> &state;
    bool &handler_deleted;
    MyAddLocalHandler(std::unique_ptr<OkState> &s, bool &hd)
      : state(s), handler_deleted(hd) {}
    void doneHandler(OkState result) override {
        state = std::make_unique<OkState>(result);
    }
    ~MyAddLocalHandler() override {
        handler_deleted = true;
    }
};

TEST_F(LocalRpcMonitorMapTest, external_add_remove_while_up) {
    add_mapping(mapping, true);
    remove_mapping(mapping, true);
}

TEST_F(LocalRpcMonitorMapTest, external_add_remove_while_down) {
    add_mapping(mapping, false);
    remove_mapping(mapping, false);
}

TEST_F(LocalRpcMonitorMapTest, server_up_down_up_down) {
    add_mapping(mapping, true);
    flip_up_state(mapping, true, 3);
    remove_mapping(mapping, false);
}

TEST_F(LocalRpcMonitorMapTest, server_down_up_down_up) {
    add_mapping(mapping, false);
    flip_up_state(mapping, false, 3);
    remove_mapping(mapping, true);
}

TEST_F(LocalRpcMonitorMapTest, multi_mapping) {
    ServiceMapping m1("dummy_service1", "dummy_spec1");
    ServiceMapping m2("dummy_service2", "dummy_spec2");
    ServiceMapping m3("dummy_service3", "dummy_spec3");
    add_mapping(m1, true);
    add_mapping(m2, false);
    add_mapping(m3, true);
    flip_up_state(m1, true, 3);
    flip_up_state(m2, false, 3);
    flip_up_state(m3, true, 3);
    remove_mapping(m1, false);
    remove_mapping(m2, true);
    remove_mapping(m3, false);
}

TEST_F(LocalRpcMonitorMapTest, local_add_ok) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::start(mapping, true)});
    map_log.expect({});
    map.up(mapping);
    monitor_log.expect({});
    map_log.expect({MapCall::add(mapping)});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_already_up) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, true);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({});
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_unknown_comes_up) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, false);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::stop(mapping), MonitorCall::start(mapping, true)});
    map_log.expect({});
    ASSERT_FALSE(state);
    map.up(mapping);
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
    map_log.expect({MapCall::add(mapping)});
}

TEST_F(LocalRpcMonitorMapTest, local_add_unknown_goes_down) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, false);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::stop(mapping), MonitorCall::start(mapping, true)});
    map_log.expect({});
    ASSERT_FALSE(state);
    map.down(mapping);
    ASSERT_TRUE(state);
    EXPECT_FALSE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_conflict) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, true);
    map.addLocal(mapping_conflict, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({});
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->failed());
    ASSERT_TRUE(handler_deleted);
}

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>extend test<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/slobrok/server/local_rpc_monitor_map.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/fnet/scheduler.h>
#include <map>

using namespace vespalib;
using namespace slobrok;
using vespalib::make_string_short::fmt;

struct MapCall {
    vespalib::string name;
    ServiceMapping mapping;
    ServiceMapping old;
    static MapCall add(const ServiceMapping &m) { return {"add", m, {"",""}}; }
    static MapCall remove(const ServiceMapping &m) { return {"remove", m, {"",""}}; }
    static MapCall update(const ServiceMapping &o, const ServiceMapping &m) { return {"update", m, o}; }
    void check(const MapCall &rhs) const {
        EXPECT_EQ(name, rhs.name);
        EXPECT_EQ(mapping, rhs.mapping);
        EXPECT_EQ(old, rhs.old);
    }
    ~MapCall();
};
MapCall::~MapCall() = default;

struct MonitorCall {
    vespalib::string name;
    ServiceMapping mapping;
    bool hurry;
    static MonitorCall start(const ServiceMapping &m, bool h) { return {"start", m, h}; }
    static MonitorCall stop(const ServiceMapping &m) { return {"stop", m, false}; }
    void check(const MonitorCall &rhs) const {
        EXPECT_EQ(name, rhs.name);
        EXPECT_EQ(mapping, rhs.mapping);
        EXPECT_EQ(hurry, rhs.hurry);
    }
    ~MonitorCall();
};
MonitorCall::~MonitorCall() = default;

template <typename Call>
class CallLog {
private:
    std::vector<Call> _calls;
    size_t _checked;
public:
    CallLog() noexcept : _calls(), _checked(0) {}
    ~CallLog() { EXPECT_EQ(_calls.size(), _checked); }
    void log(Call call) { _calls.push_back(call); }
    void expect(std::initializer_list<Call> list) {
        ASSERT_EQ(list.size(), (_calls.size() - _checked));
        for (const auto &call: list) {
            call.check(_calls[_checked++]);
        }
    }
};

struct MapLog : CallLog<MapCall>, MapListener {
    void add(const ServiceMapping &mapping) override {
        log(MapCall::add(mapping));
    }
    void remove(const ServiceMapping &mapping) override {
        log(MapCall::remove(mapping));
    }
    void update(const ServiceMapping &old_mapping,
                const ServiceMapping &new_mapping) override
    {
        log(MapCall::update(old_mapping, new_mapping));
    }
};

struct MonitorLog : CallLog<MonitorCall>, MappingMonitor {
    void start(const ServiceMapping& mapping, bool hurry) override {
        log(MonitorCall::start(mapping, hurry));
    }
    void stop(const ServiceMapping& mapping) override {
        log(MonitorCall::stop(mapping));
    }
};

struct MyMappingMonitor : MappingMonitor {
    MonitorLog &monitor;
    MyMappingMonitor(MonitorLog &m) : monitor(m) {}
    void start(const ServiceMapping& mapping, bool hurry) override {
        monitor.start(mapping, hurry);
    }
    void stop(const ServiceMapping& mapping) override {
        monitor.stop(mapping);
    }
};

struct LocalRpcMonitorMapTest : public ::testing::Test {
    steady_time time;
    FNET_Scheduler scheduler;
    MonitorLog monitor_log;
    MapLog map_log;
    LocalRpcMonitorMap map;
    std::unique_ptr<MapSubscription> subscription;
    ServiceMapping mapping;
    ServiceMapping mapping_conflict;
    LocalRpcMonitorMapTest()
      : time(duration::zero()),
        scheduler(&time, &time), monitor_log(), map_log(),
        map(&scheduler, [this](auto &owner)
            {
                EXPECT_EQ(&owner, &map);
                return std::make_unique<MyMappingMonitor>(monitor_log);
            }),
        subscription(MapSubscription::subscribe(map.dispatcher(), map_log)),
        mapping("dummy_service", "dummy_spec"),
        mapping_conflict("dummy_service", "conflicting_dummy_spec")
    {}
    void tick(duration elapsed = FNET_Scheduler::tick_ms) {
        time += elapsed;
        scheduler.CheckTasks();
    }
    void add_mapping(const ServiceMapping &m, bool is_up) {
        map.add(m); // <- add from consensus map
        monitor_log.expect({});
        tick(0ms); // <- process delayed add event
        monitor_log.expect({MonitorCall::start(m, false)});
        map_log.expect({});
        if (is_up) {
            map.up(m); // <- up from monitor
            map_log.expect({MapCall::add(m)});
        } else {
            map.down(m); // <- down from monitor
            map_log.expect({});
        }
    }
    void flip_up_state(const ServiceMapping &m, bool was_up, size_t cnt) {
        for (size_t i = 0; i < cnt; ++i) {
            if (was_up) {
                map.up(m);
                map_log.expect({});
                map.down(m);
                map_log.expect({MapCall::remove(m)});
            } else {
                map.down(m);
                map_log.expect({});
                map.up(m);
                map_log.expect({MapCall::add(m)});
            }
            was_up = !was_up;
        }
        monitor_log.expect({});
    }
    void remove_mapping(const ServiceMapping &m, bool was_up) {
        map.remove(m); // <- remove from consensus map
        monitor_log.expect({});
        tick(0ms); // <- process delayed remove event
        monitor_log.expect({MonitorCall::stop(m)});
        if (was_up) {
            map_log.expect({MapCall::remove(m)});
        } else {
            map_log.expect({});
        }
    }
    ~LocalRpcMonitorMapTest();
};
LocalRpcMonitorMapTest::~LocalRpcMonitorMapTest() = default;

struct MyAddLocalHandler : LocalRpcMonitorMap::AddLocalCompletionHandler {
    std::unique_ptr<OkState> &state;
    bool &handler_deleted;
    MyAddLocalHandler(std::unique_ptr<OkState> &s, bool &hd)
      : state(s), handler_deleted(hd) {}
    void doneHandler(OkState result) override {
        state = std::make_unique<OkState>(result);
    }
    ~MyAddLocalHandler() override {
        handler_deleted = true;
    }
};

TEST_F(LocalRpcMonitorMapTest, external_add_remove_while_up) {
    add_mapping(mapping, true);
    remove_mapping(mapping, true);
}

TEST_F(LocalRpcMonitorMapTest, external_add_remove_while_down) {
    add_mapping(mapping, false);
    remove_mapping(mapping, false);
}

TEST_F(LocalRpcMonitorMapTest, server_up_down_up_down) {
    add_mapping(mapping, true);
    flip_up_state(mapping, true, 3);
    remove_mapping(mapping, false);
}

TEST_F(LocalRpcMonitorMapTest, server_down_up_down_up) {
    add_mapping(mapping, false);
    flip_up_state(mapping, false, 3);
    remove_mapping(mapping, true);
}

TEST_F(LocalRpcMonitorMapTest, multi_mapping) {
    ServiceMapping m1("dummy_service1", "dummy_spec1");
    ServiceMapping m2("dummy_service2", "dummy_spec2");
    ServiceMapping m3("dummy_service3", "dummy_spec3");
    add_mapping(m1, true);
    add_mapping(m2, false);
    add_mapping(m3, true);
    flip_up_state(m1, true, 3);
    flip_up_state(m2, false, 3);
    flip_up_state(m3, true, 3);
    remove_mapping(m1, false);
    remove_mapping(m2, true);
    remove_mapping(m3, false);
}

TEST_F(LocalRpcMonitorMapTest, local_add_ok) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::start(mapping, true)});
    map_log.expect({});
    map.up(mapping);
    monitor_log.expect({});
    map_log.expect({MapCall::add(mapping)});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_already_up) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, true);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({});
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_unknown_comes_up) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, false);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::stop(mapping), MonitorCall::start(mapping, true)});
    map_log.expect({});
    EXPECT_FALSE(state);
    map.up(mapping);
    map_log.expect({MapCall::add(mapping)});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_unknown_goes_down) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, false);
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::stop(mapping), MonitorCall::start(mapping, true)});
    map_log.expect({});
    EXPECT_FALSE(state);
    map.down(mapping);
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_FALSE(state->ok());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_add_conflict) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    add_mapping(mapping, true);
    map.addLocal(mapping_conflict, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({});
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->failed());
    ASSERT_TRUE(handler_deleted);
}

TEST_F(LocalRpcMonitorMapTest, local_multi_add) {
    std::unique_ptr<OkState> state1;
    bool handler_deleted1;
    std::unique_ptr<OkState> state2;
    bool handler_deleted2;
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state1, handler_deleted1));
    monitor_log.expect({MonitorCall::start(mapping, true)});
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state2, handler_deleted2));
    monitor_log.expect({});
    map_log.expect({});
    EXPECT_FALSE(state1);
    EXPECT_FALSE(state2);
    map.up(mapping);
    monitor_log.expect({});
    map_log.expect({MapCall::add(mapping)});
    ASSERT_TRUE(state1);
    ASSERT_TRUE(state2);
    EXPECT_TRUE(state1->ok());
    EXPECT_TRUE(state2->ok());
    ASSERT_TRUE(handler_deleted1);
    ASSERT_TRUE(handler_deleted2);
}

TEST_F(LocalRpcMonitorMapTest, local_remove) {
    add_mapping(mapping, true);
    map.removeLocal(mapping);
    monitor_log.expect({MonitorCall::stop(mapping), MonitorCall::start(mapping, false)});
    map_log.expect({MapCall::remove(mapping)});
    map.up(mapping); // timeout case (should normally not happen)
    map_log.expect({MapCall::add(mapping)});
}

TEST_F(LocalRpcMonitorMapTest, local_add_local_remove) {
    std::unique_ptr<OkState> state;
    bool handler_deleted;
    map.addLocal(mapping, std::make_unique<MyAddLocalHandler>(state, handler_deleted));
    monitor_log.expect({MonitorCall::start(mapping, true)});
    map_log.expect({});
    map.removeLocal(mapping);
    monitor_log.expect({MonitorCall::stop(mapping)});
    map_log.expect({});
    ASSERT_TRUE(state);
    EXPECT_TRUE(state->failed());
    ASSERT_TRUE(handler_deleted);
}

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/plugin_group.h"

#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "webkit/glue/plugins/plugin_list.h"

#if defined(OS_MACOSX)
// Plugin Groups for Mac.
// Plugins are listed here as soon as vulnerabilities and solutions
// (new versions) are published.
// TODO(panayiotis): Get the Real Player version on Mac, somehow.
static const PluginGroupDefinition kGroupDefinitions[] = {
  { "Quicktime", "QuickTime Plug-in", "", "", "7.6.6",
    "http://www.apple.com/quicktime/download/" },
  { "Java", "Java", "", "", "",
    "http://support.apple.com/kb/HT1338" },
  { "Flash", "Shockwave Flash", "", "", "10.1.82",
    "http://get.adobe.com/flashplayer/" },
  { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Silverlight 4", "Silverlight", "4", "5", "",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Flip4Mac", "Flip4Mac", "",  "", "2.2.1",
    "http://www.telestream.net/flip4mac-wmv/overview.htm" },
  { "Shockwave", "Shockwave for Director", "",  "", "11.5.7.609",
    "http://www.adobe.com/shockwave/download/" }
};

#elif defined(OS_WIN)
// TODO(panayiotis): We should group "RealJukebox NS Plugin" with the rest of
// the RealPlayer files.
static const PluginGroupDefinition kGroupDefinitions[] = {
  { "Quicktime", "QuickTime Plug-in", "", "", "7.6.6",
    "http://www.apple.com/quicktime/download/" },
  { "Java 6", "Java", "", "6", "6.0.200",
    "http://www.java.com/" },
  { "Adobe Reader 9", "Adobe Acrobat", "9", "10", "9.3.3",
    "http://get.adobe.com/reader/" },
  { "Adobe Reader 8", "Adobe Acrobat", "0", "9",  "8.2.3",
    "http://get.adobe.com/reader/" },
  { "Flash", "Shockwave Flash", "", "", "10.1.82",
    "http://get.adobe.com/flashplayer/" },
  { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Silverlight 4", "Silverlight", "4", "5", "",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Shockwave", "Shockwave for Director", "", "", "11.5.7.609",
    "http://www.adobe.com/shockwave/download/" },
  { "DivX Player", "DivX Web Player", "",  "", "1.4.3.4",
    "http://download.divx.com/divx/autoupdate/player/DivXWebPlayerInstaller.exe" },
  // These are here for grouping, no vulnerabilies known.
  { "Windows Media Player",  "Windows Media Player",  "",  "",   "", "" },
  { "Microsoft Office",  "Microsoft Office",  "",  "",   "", "" },
  // TODO(panayiotis): The vulnerable versions are
  //  (v >=  6.0.12.1040 && v <= 6.0.12.1663)
  //  || v == 6.0.12.1698  || v == 6.0.12.1741
  { "RealPlayer", "RealPlayer", "",  "",  "",
    "http://www.adobe.com/shockwave/download/" },
};

#else
static const PluginGroupDefinition kGroupDefinitions[] = {};
#endif

/*static*/
std::set<string16>* PluginGroup::policy_disabled_puglins_;

/*static*/
const PluginGroupDefinition* PluginGroup::GetPluginGroupDefinitions() {
  return kGroupDefinitions;
}

/*static*/
size_t PluginGroup::GetPluginGroupDefinitionsSize() {
  // TODO(viettrungluu): |arraysize()| doesn't work with zero-size arrays.
  return ARRAYSIZE_UNSAFE(kGroupDefinitions);
}

/*static*/
void PluginGroup::SetPolicyDisabledPluginSet(const std::set<string16>& set) {
  if (!policy_disabled_puglins_) {
    policy_disabled_puglins_ = new std::set<string16>();
    *policy_disabled_puglins_ = set;
  }
}

/*static*/
bool PluginGroup::IsPluginNameDisabledByPolicy(const string16& plugin_name) {
  return policy_disabled_puglins_ &&
      policy_disabled_puglins_->find(plugin_name) !=
          policy_disabled_puglins_->end();
}

/*static*/
bool PluginGroup::IsPluginPathDisabledByPolicy(const FilePath& plugin_path) {
  std::vector<WebPluginInfo> plugins;
  NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
  for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
       it != plugins.end();
       ++it) {
    if (FilePath::CompareEqualIgnoreCase(it->path.value(),
        plugin_path.value()) && IsPluginNameDisabledByPolicy(it->name)) {
      return true;
    }
  }
  return false;
}

PluginGroup::PluginGroup(const string16& group_name,
                         const string16& name_matcher,
                         const std::string& version_range_low,
                         const std::string& version_range_high,
                         const std::string& min_version,
                         const std::string& update_url) {
  group_name_ = group_name;
  name_matcher_ = name_matcher;
  version_range_low_str_ = version_range_low;
  if (!version_range_low.empty()) {
    version_range_low_.reset(
        Version::GetVersionFromString(version_range_low));
  }
  version_range_high_str_ = version_range_high;
  if (!version_range_high.empty()) {
    version_range_high_.reset(
        Version::GetVersionFromString(version_range_high));
  }
  min_version_str_ = min_version;
  if (!min_version.empty()) {
    min_version_.reset(Version::GetVersionFromString(min_version));
  }
  update_url_ = update_url;
  enabled_ = false;
  version_.reset(Version::GetVersionFromString("0"));
}

PluginGroup* PluginGroup::FromPluginGroupDefinition(
    const PluginGroupDefinition& definition) {
  return new PluginGroup(ASCIIToUTF16(definition.name),
                         ASCIIToUTF16(definition.name_matcher),
                         definition.version_matcher_low,
                         definition.version_matcher_high,
                         definition.min_version,
                         definition.update_url);
}

PluginGroup::~PluginGroup() { }

PluginGroup* PluginGroup::FromWebPluginInfo(const WebPluginInfo& wpi) {
  // Create a matcher from the name of this plugin.
  return new PluginGroup(wpi.name, wpi.name,
                         "", "", "", "");
}

PluginGroup* PluginGroup::FindHardcodedPluginGroup(const WebPluginInfo& info) {
  static std::vector<linked_ptr<PluginGroup> >* hardcoded_plugin_groups = NULL;
  if (!hardcoded_plugin_groups) {
    std::vector<linked_ptr<PluginGroup> >* groups =
        new std::vector<linked_ptr<PluginGroup> >();
    const PluginGroupDefinition* definitions = GetPluginGroupDefinitions();
    for (size_t i = 0; i < GetPluginGroupDefinitionsSize(); ++i) {
      PluginGroup* definition_group = PluginGroup::FromPluginGroupDefinition(
          definitions[i]);
      groups->push_back(linked_ptr<PluginGroup>(definition_group));
    }
    hardcoded_plugin_groups = groups;
  }

  // See if this plugin matches any of the hardcoded groups.
  PluginGroup* hardcoded_group = FindGroupMatchingPlugin(
      *hardcoded_plugin_groups, info);
  if (hardcoded_group) {
    // Make a copy.
    return hardcoded_group->Copy();
  } else {
    // Not found in our hardcoded list, create a new one.
    return PluginGroup::FromWebPluginInfo(info);
  }
}

PluginGroup* PluginGroup::FindGroupMatchingPlugin(
    std::vector<linked_ptr<PluginGroup> >& plugin_groups,
    const WebPluginInfo& plugin) {
  for (std::vector<linked_ptr<PluginGroup> >::iterator it =
       plugin_groups.begin();
       it != plugin_groups.end();
       ++it) {
    if ((*it)->Match(plugin)) {
      return it->get();
    }
  }
  return NULL;
}

bool PluginGroup::Match(const WebPluginInfo& plugin) const {
  if (name_matcher_.empty()) {
    return false;
  }

  // Look for the name matcher anywhere in the plugin name.
  if (plugin.name.find(name_matcher_) == string16::npos) {
    return false;
  }

  if (version_range_low_.get() == NULL ||
      version_range_high_.get() == NULL) {
    return true;
  }

  // There's a version range, we must be in it.
  scoped_ptr<Version> plugin_version(
      Version::GetVersionFromString(UTF16ToWide(plugin.version)));
  if (plugin_version.get() == NULL) {
    // No version could be extracted, assume we don't match the range.
    return false;
  }

  // We match if we are in the range: [low, high)
  return (version_range_low_->CompareTo(*plugin_version) <= 0 &&
          version_range_high_->CompareTo(*plugin_version) > 0);
}

void PluginGroup::UpdateDescriptionAndVersion(const WebPluginInfo& plugin) {
  description_ = plugin.desc;

  // Update |version_|. Remove spaces and ')' from the version string,
  // Replace any instances of 'r', ',' or '(' with a dot.
  std::wstring version = UTF16ToWide(plugin.version);
  RemoveChars(version, L") ", &version);
  std::replace(version.begin(), version.end(), 'r', '.');
  std::replace(version.begin(), version.end(), ',', '.');
  std::replace(version.begin(), version.end(), '(', '.');

  if (Version* new_version = Version::GetVersionFromString(version))
    version_.reset(new_version);
  else
    version_.reset(Version::GetVersionFromString("0"));
}

void PluginGroup::AddPlugin(const WebPluginInfo& plugin, int position) {
  web_plugin_infos_.push_back(plugin);
  // The position of this plugin relative to the global list of plugins.
  web_plugin_positions_.push_back(position);

  // A group is enabled if any of the files are enabled.
  if (plugin.enabled) {
    if (!enabled_) {
      // If this is the first enabled plugin, use its description.
      enabled_ = true;
      UpdateDescriptionAndVersion(plugin);
    }
  } else {
    // If this is the first plugin and it's disabled,
    // use its description for now.
    if (description_.empty())
      UpdateDescriptionAndVersion(plugin);
  }
}

DictionaryValue* PluginGroup::GetSummary() const {
  DictionaryValue* result = new DictionaryValue();
  result->SetString("name", group_name_);
  result->SetBoolean("enabled", enabled_);
  return result;
}

DictionaryValue* PluginGroup::GetDataForUI() const {
  DictionaryValue* result = new DictionaryValue();
  result->SetString("name", group_name_);
  result->SetString("description", description_);
  result->SetString("version", version_->GetString());
  result->SetString("update_url", update_url_);
  result->SetBoolean("critical", IsVulnerable());

  bool group_disabled_by_policy = IsPluginNameDisabledByPolicy(group_name_);
  if (group_disabled_by_policy) {
    result->SetString("enabledMode", "disabledByPolicy");
  } else {
    result->SetString("enabledMode", enabled_ ? "enabled" : "disabledByUser");
  }

  ListValue* plugin_files = new ListValue();
  for (size_t i = 0; i < web_plugin_infos_.size(); ++i) {
    const WebPluginInfo& web_plugin = web_plugin_infos_[i];
    int priority = web_plugin_positions_[i];
    DictionaryValue* plugin_file = new DictionaryValue();
    plugin_file->SetString("name", web_plugin.name);
    plugin_file->SetString("description", web_plugin.desc);
    plugin_file->SetString("path", web_plugin.path.value());
    plugin_file->SetString("version", web_plugin.version);
    bool plugin_disabled_by_policy = group_disabled_by_policy ||
        IsPluginNameDisabledByPolicy(web_plugin.name);
    if (plugin_disabled_by_policy) {
      plugin_file->SetString("enabledMode", "disabledByPolicy");
    } else {
      plugin_file->SetString("enabledMode",
                             web_plugin.enabled ? "enabled" : "disabledByUser");
    }
    plugin_file->SetInteger("priority", priority);

    ListValue* mime_types = new ListValue();
    for (std::vector<WebPluginMimeType>::const_iterator type_it =
         web_plugin.mime_types.begin();
         type_it != web_plugin.mime_types.end();
         ++type_it) {
      DictionaryValue* mime_type = new DictionaryValue();
      mime_type->SetString("mimeType", type_it->mime_type);
      mime_type->SetString("description", type_it->description);

      ListValue* file_extensions = new ListValue();
      for (std::vector<std::string>::const_iterator ext_it =
           type_it->file_extensions.begin();
           ext_it != type_it->file_extensions.end();
           ++ext_it) {
        file_extensions->Append(new StringValue(*ext_it));
      }
      mime_type->Set("fileExtensions", file_extensions);

      mime_types->Append(mime_type);
    }
    plugin_file->Set("mimeTypes", mime_types);

    plugin_files->Append(plugin_file);
  }
  result->Set("plugin_files", plugin_files);

  return result;
}

// Returns true if the latest version of this plugin group is vulnerable.
bool PluginGroup::IsVulnerable() const {
  if (min_version_.get() == NULL || version_->GetString() == "0") {
    return false;
  }
  return version_->CompareTo(*min_version_) < 0;
}

void PluginGroup::Enable(bool enable) {
  for (std::vector<WebPluginInfo>::const_iterator it =
       web_plugin_infos_.begin();
       it != web_plugin_infos_.end(); ++it) {
    if (enable && !IsPluginNameDisabledByPolicy(it->name)) {
      NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(it->path));
    } else {
      NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(it->path));
    }
  }
}

<commit_msg>Update latest secure version of Quicktime for Windows. See http://support.apple.com/kb/HT4290 BUG=None TEST=unit_tests<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/plugin_group.h"

#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "webkit/glue/plugins/plugin_list.h"

#if defined(OS_MACOSX)
// Plugin Groups for Mac.
// Plugins are listed here as soon as vulnerabilities and solutions
// (new versions) are published.
// TODO(panayiotis): Get the Real Player version on Mac, somehow.
static const PluginGroupDefinition kGroupDefinitions[] = {
  { "Quicktime", "QuickTime Plug-in", "", "", "7.6.6",
    "http://www.apple.com/quicktime/download/" },
  { "Java", "Java", "", "", "",
    "http://support.apple.com/kb/HT1338" },
  { "Flash", "Shockwave Flash", "", "", "10.1.82",
    "http://get.adobe.com/flashplayer/" },
  { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Silverlight 4", "Silverlight", "4", "5", "",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Flip4Mac", "Flip4Mac", "",  "", "2.2.1",
    "http://www.telestream.net/flip4mac-wmv/overview.htm" },
  { "Shockwave", "Shockwave for Director", "",  "", "11.5.7.609",
    "http://www.adobe.com/shockwave/download/" }
};

#elif defined(OS_WIN)
// TODO(panayiotis): We should group "RealJukebox NS Plugin" with the rest of
// the RealPlayer files.
static const PluginGroupDefinition kGroupDefinitions[] = {
  { "Quicktime", "QuickTime Plug-in", "", "", "7.6.7",
    "http://www.apple.com/quicktime/download/" },
  { "Java 6", "Java", "", "6", "6.0.200",
    "http://www.java.com/" },
  { "Adobe Reader 9", "Adobe Acrobat", "9", "10", "9.3.3",
    "http://get.adobe.com/reader/" },
  { "Adobe Reader 8", "Adobe Acrobat", "0", "9",  "8.2.3",
    "http://get.adobe.com/reader/" },
  { "Flash", "Shockwave Flash", "", "", "10.1.82",
    "http://get.adobe.com/flashplayer/" },
  { "Silverlight 3", "Silverlight", "0", "4", "3.0.50106.0",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Silverlight 4", "Silverlight", "4", "5", "",
    "http://go.microsoft.com/fwlink/?LinkID=185927" },
  { "Shockwave", "Shockwave for Director", "", "", "11.5.7.609",
    "http://www.adobe.com/shockwave/download/" },
  { "DivX Player", "DivX Web Player", "",  "", "1.4.3.4",
    "http://download.divx.com/divx/autoupdate/player/DivXWebPlayerInstaller.exe" },
  // These are here for grouping, no vulnerabilies known.
  { "Windows Media Player",  "Windows Media Player",  "",  "",   "", "" },
  { "Microsoft Office",  "Microsoft Office",  "",  "",   "", "" },
  // TODO(panayiotis): The vulnerable versions are
  //  (v >=  6.0.12.1040 && v <= 6.0.12.1663)
  //  || v == 6.0.12.1698  || v == 6.0.12.1741
  { "RealPlayer", "RealPlayer", "",  "",  "",
    "http://www.adobe.com/shockwave/download/" },
};

#else
static const PluginGroupDefinition kGroupDefinitions[] = {};
#endif

/*static*/
std::set<string16>* PluginGroup::policy_disabled_puglins_;

/*static*/
const PluginGroupDefinition* PluginGroup::GetPluginGroupDefinitions() {
  return kGroupDefinitions;
}

/*static*/
size_t PluginGroup::GetPluginGroupDefinitionsSize() {
  // TODO(viettrungluu): |arraysize()| doesn't work with zero-size arrays.
  return ARRAYSIZE_UNSAFE(kGroupDefinitions);
}

/*static*/
void PluginGroup::SetPolicyDisabledPluginSet(const std::set<string16>& set) {
  if (!policy_disabled_puglins_) {
    policy_disabled_puglins_ = new std::set<string16>();
    *policy_disabled_puglins_ = set;
  }
}

/*static*/
bool PluginGroup::IsPluginNameDisabledByPolicy(const string16& plugin_name) {
  return policy_disabled_puglins_ &&
      policy_disabled_puglins_->find(plugin_name) !=
          policy_disabled_puglins_->end();
}

/*static*/
bool PluginGroup::IsPluginPathDisabledByPolicy(const FilePath& plugin_path) {
  std::vector<WebPluginInfo> plugins;
  NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
  for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
       it != plugins.end();
       ++it) {
    if (FilePath::CompareEqualIgnoreCase(it->path.value(),
        plugin_path.value()) && IsPluginNameDisabledByPolicy(it->name)) {
      return true;
    }
  }
  return false;
}

PluginGroup::PluginGroup(const string16& group_name,
                         const string16& name_matcher,
                         const std::string& version_range_low,
                         const std::string& version_range_high,
                         const std::string& min_version,
                         const std::string& update_url) {
  group_name_ = group_name;
  name_matcher_ = name_matcher;
  version_range_low_str_ = version_range_low;
  if (!version_range_low.empty()) {
    version_range_low_.reset(
        Version::GetVersionFromString(version_range_low));
  }
  version_range_high_str_ = version_range_high;
  if (!version_range_high.empty()) {
    version_range_high_.reset(
        Version::GetVersionFromString(version_range_high));
  }
  min_version_str_ = min_version;
  if (!min_version.empty()) {
    min_version_.reset(Version::GetVersionFromString(min_version));
  }
  update_url_ = update_url;
  enabled_ = false;
  version_.reset(Version::GetVersionFromString("0"));
}

PluginGroup* PluginGroup::FromPluginGroupDefinition(
    const PluginGroupDefinition& definition) {
  return new PluginGroup(ASCIIToUTF16(definition.name),
                         ASCIIToUTF16(definition.name_matcher),
                         definition.version_matcher_low,
                         definition.version_matcher_high,
                         definition.min_version,
                         definition.update_url);
}

PluginGroup::~PluginGroup() { }

PluginGroup* PluginGroup::FromWebPluginInfo(const WebPluginInfo& wpi) {
  // Create a matcher from the name of this plugin.
  return new PluginGroup(wpi.name, wpi.name,
                         "", "", "", "");
}

PluginGroup* PluginGroup::FindHardcodedPluginGroup(const WebPluginInfo& info) {
  static std::vector<linked_ptr<PluginGroup> >* hardcoded_plugin_groups = NULL;
  if (!hardcoded_plugin_groups) {
    std::vector<linked_ptr<PluginGroup> >* groups =
        new std::vector<linked_ptr<PluginGroup> >();
    const PluginGroupDefinition* definitions = GetPluginGroupDefinitions();
    for (size_t i = 0; i < GetPluginGroupDefinitionsSize(); ++i) {
      PluginGroup* definition_group = PluginGroup::FromPluginGroupDefinition(
          definitions[i]);
      groups->push_back(linked_ptr<PluginGroup>(definition_group));
    }
    hardcoded_plugin_groups = groups;
  }

  // See if this plugin matches any of the hardcoded groups.
  PluginGroup* hardcoded_group = FindGroupMatchingPlugin(
      *hardcoded_plugin_groups, info);
  if (hardcoded_group) {
    // Make a copy.
    return hardcoded_group->Copy();
  } else {
    // Not found in our hardcoded list, create a new one.
    return PluginGroup::FromWebPluginInfo(info);
  }
}

PluginGroup* PluginGroup::FindGroupMatchingPlugin(
    std::vector<linked_ptr<PluginGroup> >& plugin_groups,
    const WebPluginInfo& plugin) {
  for (std::vector<linked_ptr<PluginGroup> >::iterator it =
       plugin_groups.begin();
       it != plugin_groups.end();
       ++it) {
    if ((*it)->Match(plugin)) {
      return it->get();
    }
  }
  return NULL;
}

bool PluginGroup::Match(const WebPluginInfo& plugin) const {
  if (name_matcher_.empty()) {
    return false;
  }

  // Look for the name matcher anywhere in the plugin name.
  if (plugin.name.find(name_matcher_) == string16::npos) {
    return false;
  }

  if (version_range_low_.get() == NULL ||
      version_range_high_.get() == NULL) {
    return true;
  }

  // There's a version range, we must be in it.
  scoped_ptr<Version> plugin_version(
      Version::GetVersionFromString(UTF16ToWide(plugin.version)));
  if (plugin_version.get() == NULL) {
    // No version could be extracted, assume we don't match the range.
    return false;
  }

  // We match if we are in the range: [low, high)
  return (version_range_low_->CompareTo(*plugin_version) <= 0 &&
          version_range_high_->CompareTo(*plugin_version) > 0);
}

void PluginGroup::UpdateDescriptionAndVersion(const WebPluginInfo& plugin) {
  description_ = plugin.desc;

  // Update |version_|. Remove spaces and ')' from the version string,
  // Replace any instances of 'r', ',' or '(' with a dot.
  std::wstring version = UTF16ToWide(plugin.version);
  RemoveChars(version, L") ", &version);
  std::replace(version.begin(), version.end(), 'r', '.');
  std::replace(version.begin(), version.end(), ',', '.');
  std::replace(version.begin(), version.end(), '(', '.');

  if (Version* new_version = Version::GetVersionFromString(version))
    version_.reset(new_version);
  else
    version_.reset(Version::GetVersionFromString("0"));
}

void PluginGroup::AddPlugin(const WebPluginInfo& plugin, int position) {
  web_plugin_infos_.push_back(plugin);
  // The position of this plugin relative to the global list of plugins.
  web_plugin_positions_.push_back(position);

  // A group is enabled if any of the files are enabled.
  if (plugin.enabled) {
    if (!enabled_) {
      // If this is the first enabled plugin, use its description.
      enabled_ = true;
      UpdateDescriptionAndVersion(plugin);
    }
  } else {
    // If this is the first plugin and it's disabled,
    // use its description for now.
    if (description_.empty())
      UpdateDescriptionAndVersion(plugin);
  }
}

DictionaryValue* PluginGroup::GetSummary() const {
  DictionaryValue* result = new DictionaryValue();
  result->SetString("name", group_name_);
  result->SetBoolean("enabled", enabled_);
  return result;
}

DictionaryValue* PluginGroup::GetDataForUI() const {
  DictionaryValue* result = new DictionaryValue();
  result->SetString("name", group_name_);
  result->SetString("description", description_);
  result->SetString("version", version_->GetString());
  result->SetString("update_url", update_url_);
  result->SetBoolean("critical", IsVulnerable());

  bool group_disabled_by_policy = IsPluginNameDisabledByPolicy(group_name_);
  if (group_disabled_by_policy) {
    result->SetString("enabledMode", "disabledByPolicy");
  } else {
    result->SetString("enabledMode", enabled_ ? "enabled" : "disabledByUser");
  }

  ListValue* plugin_files = new ListValue();
  for (size_t i = 0; i < web_plugin_infos_.size(); ++i) {
    const WebPluginInfo& web_plugin = web_plugin_infos_[i];
    int priority = web_plugin_positions_[i];
    DictionaryValue* plugin_file = new DictionaryValue();
    plugin_file->SetString("name", web_plugin.name);
    plugin_file->SetString("description", web_plugin.desc);
    plugin_file->SetString("path", web_plugin.path.value());
    plugin_file->SetString("version", web_plugin.version);
    bool plugin_disabled_by_policy = group_disabled_by_policy ||
        IsPluginNameDisabledByPolicy(web_plugin.name);
    if (plugin_disabled_by_policy) {
      plugin_file->SetString("enabledMode", "disabledByPolicy");
    } else {
      plugin_file->SetString("enabledMode",
                             web_plugin.enabled ? "enabled" : "disabledByUser");
    }
    plugin_file->SetInteger("priority", priority);

    ListValue* mime_types = new ListValue();
    for (std::vector<WebPluginMimeType>::const_iterator type_it =
         web_plugin.mime_types.begin();
         type_it != web_plugin.mime_types.end();
         ++type_it) {
      DictionaryValue* mime_type = new DictionaryValue();
      mime_type->SetString("mimeType", type_it->mime_type);
      mime_type->SetString("description", type_it->description);

      ListValue* file_extensions = new ListValue();
      for (std::vector<std::string>::const_iterator ext_it =
           type_it->file_extensions.begin();
           ext_it != type_it->file_extensions.end();
           ++ext_it) {
        file_extensions->Append(new StringValue(*ext_it));
      }
      mime_type->Set("fileExtensions", file_extensions);

      mime_types->Append(mime_type);
    }
    plugin_file->Set("mimeTypes", mime_types);

    plugin_files->Append(plugin_file);
  }
  result->Set("plugin_files", plugin_files);

  return result;
}

// Returns true if the latest version of this plugin group is vulnerable.
bool PluginGroup::IsVulnerable() const {
  if (min_version_.get() == NULL || version_->GetString() == "0") {
    return false;
  }
  return version_->CompareTo(*min_version_) < 0;
}

void PluginGroup::Enable(bool enable) {
  for (std::vector<WebPluginInfo>::const_iterator it =
       web_plugin_infos_.begin();
       it != web_plugin_infos_.end(); ++it) {
    if (enable && !IsPluginNameDisabledByPolicy(it->name)) {
      NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(it->path));
    } else {
      NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(it->path));
    }
  }
}
<|endoftext|>