<commit_before>
#include "geometry/epoch.hpp"
#include "geometry/named_quantities.hpp"
#include "geometry/grassmann.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "integrators/symplectic_runge_kutta_nyström_integrator.hpp"
#include "physics/solar_system.hpp"
#include "physics/ephemeris.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/numerics.hpp"

namespace principia {

using astronomy::ICRFJ2000Equator;
using geometry::JulianDate;
using geometry::Sign;
using integrators::McLachlanAtela1992Order5Optimal;
using physics::Ephemeris;
using quantities::ArcCos;
using quantities::si::Day;
using quantities::si::Kilo;
using quantities::si::Metre;
using quantities::si::Milli;
using quantities::si::Minute;
using quantities::si::Nano;
using testing_utilities::AbsoluteError;
using ::testing::AllOf;
using ::testing::Gt;
using ::testing::Lt;

namespace physics {

namespace {
Sign const U14 = Sign(1);
Sign const U23 = Sign(-1);
}  // namespace

class EclipseTest : public testing::Test {
 protected:
  EclipseTest() {
    solar_system_1950_.Initialize(
        SOLUTION_DIR / "astronomy" / "gravity_model.proto.txt",
        SOLUTION_DIR / "astronomy" /
            "initial_state_jd_2433282_500000000.proto.txt");
  }

  void CheckLunarUmbralEclipse(Instant const& current_time,
                               Sign const moon_offset_sign) {

    //ephemeris_->Prolong(current_time +
    //                   1 * Day);  // Prolong 1 day past date of eclipse.
    auto const sun = solar_system_1950_.massive_body(*ephemeris_, "Sun");
    auto const earth = solar_system_1950_.massive_body(*ephemeris_, "Earth");
    auto const moon = solar_system_1950_.massive_body(*ephemeris_, "Moon");

    auto const q_sun = ephemeris_->trajectory(sun)
                           ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_moon = ephemeris_->trajectory(moon)
                            ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_earth = ephemeris_->trajectory(earth)
                             ->EvaluatePosition(current_time, /*hint=*/nullptr);

    // MassiveBody will need radius information.  Or non-hardcoded data from
    // https://github.com/mockingbirdnest/Principia/blob/master/astronomy/gravity_model.proto.txt
    auto const r_sun = 696000.0 * Kilo(Metre);
    auto const r_earth = 6378.1363 * Kilo(Metre);
    auto const r_moon = 1738.0 * Kilo(Metre);

    // Checking body angles at the target time.
    // Angle formed by a right circular cone with sides defined by tangent lines
    // between Sun and Earth, and axis running through the centers of each.
    auto const umbral_half_aperture =
        ArcSin((r_sun - r_earth) / (q_sun - q_earth).Norm());
    auto const apex_of_moon_locus_at_umbral_contact =
        q_earth +
        Normalize(q_earth - q_sun) * (r_earth + moon_offset_sign * r_moon) /
            Sin(umbral_half_aperture);
    // Angle between Earth and Moon as seen at
    // apex_of_moon_locus_at_umbral_contact.
    auto const earth_moon_angle =
        ArcCos(InnerProduct(apex_of_moon_locus_at_umbral_contact - q_earth,
                            apex_of_moon_locus_at_umbral_contact - q_moon) /
               ((apex_of_moon_locus_at_umbral_contact - q_moon).Norm() *
                (apex_of_moon_locus_at_umbral_contact - q_earth).Norm()));
    // We are at the desired contact if the angle between Earth and Moon from
    // the apex of locus of the moon at that contact is the same value as the
    // half-aperture of the umbra (Earth-Sun cone).
    EXPECT_THAT(AbsoluteError(umbral_half_aperture, earth_moon_angle),
                AllOf(Lt(1.0 * Milli(Radian)), Gt(1.0 * Nano(Radian))))
        << NAMED(umbral_half_aperture) << ", " << NAMED(earth_moon_angle)
        << ", " << NAMED(current_time);
  }

  void CheckLunarPenumbralEclipse(Instant const& current_time,
                                  Sign const moon_offset_sign) {

    // ephemeris_->Prolong(current_time +
    //                   1 * Day);  // Prolong 1 day past date of eclipse.
    auto const sun = solar_system_1950_.massive_body(*ephemeris_, "Sun");
    auto const earth = solar_system_1950_.massive_body(*ephemeris_, "Earth");
    auto const moon = solar_system_1950_.massive_body(*ephemeris_, "Moon");

    auto const q_sun = ephemeris_->trajectory(sun)
                           ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_moon = ephemeris_->trajectory(moon)
                            ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_earth = ephemeris_->trajectory(earth)
                             ->EvaluatePosition(current_time, /*hint=*/nullptr);

    // MassiveBody will need radius information.  Or non-hardcoded data from
    // https://github.com/mockingbirdnest/Principia/blob/master/astronomy/gravity_model.proto.txt
    auto const r_sun = 696000.0 * Kilo(Metre);
    auto const r_earth = 6378.1363 * Kilo(Metre);
    auto const r_moon = 1738.0 * Kilo(Metre);

    auto const penumbral_half_aperture =
        ArcSin((r_sun + r_earth) / (q_sun - q_earth).Norm());
    auto const apex_of_moon_locus_at_penumbral_contact =
        q_earth +
        Normalize(q_sun - q_earth) * (r_earth + moon_offset_sign * r_moon) /
            Sin(penumbral_half_aperture);
    // Angle between Earth and Moon as seen at
    // apex_of_moon_locus_at_penumbral_contact.
    auto const earth_moon_angle =
        ArcCos(InnerProduct(apex_of_moon_locus_at_penumbral_contact - q_earth,
                            apex_of_moon_locus_at_penumbral_contact - q_moon) /
               ((apex_of_moon_locus_at_penumbral_contact - q_moon).Norm() *
                (apex_of_moon_locus_at_penumbral_contact - q_earth).Norm()));
    // We are at the desired contact if the angle between Earth and Moon from
    // the apex of locus of the moon at that contact is the same value as the
    // half-aperture of the penumbra.
    EXPECT_THAT(AbsoluteError(penumbral_half_aperture, earth_moon_angle),
                AllOf(Lt(1.0 * Milli(Radian)), Gt(1.0 * Nano(Radian))))
        << NAMED(penumbral_half_aperture) << ", " << NAMED(earth_moon_angle)
        << ", " << NAMED(current_time);
  }

  SolarSystem<ICRFJ2000Equator> solar_system_1950_;
  Ephemeris<ICRFJ2000Equator> ephemeris_;
  physics::Ephemeris ephemeris_ = solar_system_1950_.MakeEphemeris(McLachlanAtela1992Order5Optimal<Position<ICRFJ2000Equator>>(), 45 * Minute, 5 * Milli(Metre));
};

#if !defined(_DEBUG)
TEST_F(EclipseTest, Year1950) {
  // Dates are TDB Julian Day for 1950-04-02.
  /*auto P1 = JulianDate(2433374.25788409);  // 18:10:49 UT
  auto U1 = JulianDate(2433374.29850909);  // 19:09:19
  auto U2 = JulianDate(2433374.354979);    // 20:30:38
  auto U3 = JulianDate(2433374.37367113);  // 20:57:33
  auto U4 = JulianDate(2433374.43016419);  // 22:18:54
  auto P4 = JulianDate(2433374.47075446);  // 23:17:21

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1950-09-26.
  P1 = JulianDate(2433550.55712016);  // 01:21:43 UT
  U1 = JulianDate(2433550.60578913);  // 02:31:48
  U2 = JulianDate(2433550.66325441);  // 03:54:33
  U3 = JulianDate(2433550.69399515);  // 04:38:49
  U4 = JulianDate(2433550.75144885);  // 06:01:33
  P4 = JulianDate(2433550.800222);    // 07:11:47

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);*/
}

TEST_F(EclipseTest, Year1951) {
  // Dates are TDB Julian Day for 1951-03-23.
  /*auto P1 = JulianDate(2433728.86842806);  // 08:50:50
  auto P4 = JulianDate(2433729.01725909);  // 12:24:19

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1951-09-15.
  P1 = JulianDate(2433904.93736321);  // 10:29:16
  P4 = JulianDate(2433905.1002799);   // 14:23:52

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarPenumbralEclipse(P4, U14);*/
}

TEST_F(EclipseTest, Year1952) {
  // Dates are TDB Julian Day for 1952-02-11 (or 10 for P1).
  /*auto P1 = JulianDate(2434053.42282623);  // P1 = 22:08:20 UT
  auto U1 = JulianDate(2434053.50334705);  // U1 = 00:04:17
  auto U4 = JulianDate(2434053.55203917);  // U4 = 01:14:24
  auto P4 = JulianDate(2434053.63249055);  // P4 = 03:10:15

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1952-08-05.
  P1 = JulianDate(2434230.22830075);  // P1 = 17:28:13 UT
  U1 = JulianDate(2434230.27385631);  // U1 = 18:33:49
  U4 = JulianDate(2434230.37606695);  // U4 = 21:01:00
  P4 = JulianDate(2434230.42161093);  // P4 = 22:06:35

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);*/

  // Later on for additional accuracy: 2 * ArcTan((x_norm_y -
  // y_normx).Norm(),(x_norm_y + y_norm_x).Norm())
  // x_norm_y = x * y.Norm() and y_norm_x = y * x.Norm()

  // Future is not now: check 2048-01-01 Lunar eclipse.
  /*P1 = JulianDate(2469076.66235167);  // 03:52:39 UT
  U1 = JulianDate(2469076.71279148);  // 05:05:17
  U2 = JulianDate(2469076.76776833);  // 06:24:27
  U3 = JulianDate(2469076.80661092);  // 07:20:23
  U4 = JulianDate(2469076.86158778);  // 08:39:33
  P4 = JulianDate(2469076.91195815);  // 09:52:05

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14); */
}
#endif

}  // namespace physics
}  // namespace principia
<commit_msg>Ephemeris fixed*<commit_after>
#include "geometry/epoch.hpp"
#include "geometry/named_quantities.hpp"
#include "geometry/grassmann.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "integrators/symplectic_runge_kutta_nyström_integrator.hpp"
#include "physics/solar_system.hpp"
#include "physics/ephemeris.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/numerics.hpp"

namespace principia {

using astronomy::ICRFJ2000Equator;
using geometry::JulianDate;
using geometry::Sign;
using integrators::McLachlanAtela1992Order5Optimal;
using physics::Ephemeris;
using quantities::ArcCos;
using quantities::si::Day;
using quantities::si::Kilo;
using quantities::si::Metre;
using quantities::si::Milli;
using quantities::si::Minute;
using quantities::si::Nano;
using testing_utilities::AbsoluteError;
using ::testing::AllOf;
using ::testing::Gt;
using ::testing::Lt;

namespace physics {

namespace {
Sign const U14 = Sign(1);
Sign const U23 = Sign(-1);
}  // namespace

class EclipseTest : public testing::Test {
 protected:
  EclipseTest() {
    solar_system_1950_.Initialize(
        SOLUTION_DIR / "astronomy" / "gravity_model.proto.txt",
        SOLUTION_DIR / "astronomy" /
            "initial_state_jd_2433282_500000000.proto.txt");
    ephemeris_ = solar_system_1950_.MakeEphemeris(
        McLachlanAtela1992Order5Optimal<Position<ICRFJ2000Equator>>(),
        45 * Minute, 5 * Milli(Metre));
    ephemeris_->Prolong(JulianDate(2434378.5003725));  // Prolong to 1953-01-01
                                                       // 00:00:00 UTC. This
                                                       // *will* cause problems
                                                       // if not changed should
                                                       // more dates be added.
  }

  void CheckLunarUmbralEclipse(Instant const& current_time,
                               Sign const moon_offset_sign) {
    auto const sun = solar_system_1950_.massive_body(*ephemeris_, "Sun");
    auto const earth = solar_system_1950_.massive_body(*ephemeris_, "Earth");
    auto const moon = solar_system_1950_.massive_body(*ephemeris_, "Moon");

    auto const q_sun = ephemeris_->trajectory(sun)
                           ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_moon = ephemeris_->trajectory(moon)
                            ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_earth = ephemeris_->trajectory(earth)
                             ->EvaluatePosition(current_time, /*hint=*/nullptr);

    // MassiveBody will need radius information.  Or non-hardcoded data from
    // https://github.com/mockingbirdnest/Principia/blob/master/astronomy/gravity_model.proto.txt
    auto const r_sun = 696000.0 * Kilo(Metre);
    auto const r_earth = 6378.1363 * Kilo(Metre);
    auto const r_moon = 1738.0 * Kilo(Metre);

    // Checking body angles at the target time.
    // Angle formed by a right circular cone with sides defined by tangent lines
    // between Sun and Earth, and axis running through the centers of each.
    auto const umbral_half_aperture =
        ArcSin((r_sun - r_earth) / (q_sun - q_earth).Norm());
    auto const apex_of_moon_locus_at_umbral_contact =
        q_earth +
        Normalize(q_earth - q_sun) * (r_earth + moon_offset_sign * r_moon) /
            Sin(umbral_half_aperture);
    // Angle between Earth and Moon as seen at
    // apex_of_moon_locus_at_umbral_contact.
    auto const earth_moon_angle =
        ArcCos(InnerProduct(apex_of_moon_locus_at_umbral_contact - q_earth,
                            apex_of_moon_locus_at_umbral_contact - q_moon) /
               ((apex_of_moon_locus_at_umbral_contact - q_moon).Norm() *
                (apex_of_moon_locus_at_umbral_contact - q_earth).Norm()));
    // We are at the desired contact if the angle between Earth and Moon from
    // the apex of locus of the moon at that contact is the same value as the
    // half-aperture of the umbra (Earth-Sun cone).
    EXPECT_THAT(AbsoluteError(umbral_half_aperture, earth_moon_angle),
                AllOf(Lt(1.0 * Milli(Radian)), Gt(1.0 * Nano(Radian))))
        << NAMED(umbral_half_aperture) << ", " << NAMED(earth_moon_angle)
        << ", " << NAMED(current_time);
  }

  void CheckLunarPenumbralEclipse(Instant const& current_time,
                                  Sign const moon_offset_sign) {
    // ephemeris_->Prolong(current_time +
    //                   1 * Day);  // Prolong 1 day past date of eclipse.
    auto const sun = solar_system_1950_.massive_body(*ephemeris_, "Sun");
    auto const earth = solar_system_1950_.massive_body(*ephemeris_, "Earth");
    auto const moon = solar_system_1950_.massive_body(*ephemeris_, "Moon");

    auto const q_sun = ephemeris_->trajectory(sun)
                           ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_moon = ephemeris_->trajectory(moon)
                            ->EvaluatePosition(current_time, /*hint=*/nullptr);
    auto const q_earth = ephemeris_->trajectory(earth)
                             ->EvaluatePosition(current_time, /*hint=*/nullptr);

    // MassiveBody will need radius information.  Or non-hardcoded data from
    // https://github.com/mockingbirdnest/Principia/blob/master/astronomy/gravity_model.proto.txt
    auto const r_sun = 696000.0 * Kilo(Metre);
    auto const r_earth = 6378.1363 * Kilo(Metre);
    auto const r_moon = 1738.0 * Kilo(Metre);

    auto const penumbral_half_aperture =
        ArcSin((r_sun + r_earth) / (q_sun - q_earth).Norm());
    auto const apex_of_moon_locus_at_penumbral_contact =
        q_earth +
        Normalize(q_sun - q_earth) * (r_earth + moon_offset_sign * r_moon) /
            Sin(penumbral_half_aperture);
    // Angle between Earth and Moon as seen at
    // apex_of_moon_locus_at_penumbral_contact.
    auto const earth_moon_angle =
        ArcCos(InnerProduct(apex_of_moon_locus_at_penumbral_contact - q_earth,
                            apex_of_moon_locus_at_penumbral_contact - q_moon) /
               ((apex_of_moon_locus_at_penumbral_contact - q_moon).Norm() *
                (apex_of_moon_locus_at_penumbral_contact - q_earth).Norm()));
    // We are at the desired contact if the angle between Earth and Moon from
    // the apex of locus of the moon at that contact is the same value as the
    // half-aperture of the penumbra.
    EXPECT_THAT(AbsoluteError(penumbral_half_aperture, earth_moon_angle),
                AllOf(Lt(1.0 * Milli(Radian)), Gt(1.0 * Nano(Radian))))
        << NAMED(penumbral_half_aperture) << ", " << NAMED(earth_moon_angle)
        << ", " << NAMED(current_time);
  }

  SolarSystem<ICRFJ2000Equator> solar_system_1950_;
  std::unique_ptr<Ephemeris<ICRFJ2000Equator>> ephemeris_;
};

#if !defined(_DEBUG)
TEST_F(EclipseTest, Year1950) {
  // Dates are TDB Julian Day for 1950-04-02.
  auto P1 = JulianDate(2433374.25788409);  // 18:10:49 UT
  auto U1 = JulianDate(2433374.29850909);  // 19:09:19
  auto U2 = JulianDate(2433374.354979);    // 20:30:38
  auto U3 = JulianDate(2433374.37367113);  // 20:57:33
  auto U4 = JulianDate(2433374.43016419);  // 22:18:54
  auto P4 = JulianDate(2433374.47075446);  // 23:17:21

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1950-09-26.
  P1 = JulianDate(2433550.55712016);  // 01:21:43 UT
  U1 = JulianDate(2433550.60578913);  // 02:31:48
  U2 = JulianDate(2433550.66325441);  // 03:54:33
  U3 = JulianDate(2433550.69399515);  // 04:38:49
  U4 = JulianDate(2433550.75144885);  // 06:01:33
  P4 = JulianDate(2433550.800222);    // 07:11:47

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);
}

TEST_F(EclipseTest, Year1951) {
  // Dates are TDB Julian Day for 1951-03-23.
  auto P1 = JulianDate(2433728.86842806);  // 08:50:50
  auto P4 = JulianDate(2433729.01725909);  // 12:24:19

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1951-09-15.
  P1 = JulianDate(2433904.93736321);  // 10:29:16
  P4 = JulianDate(2433905.1002799);   // 14:23:52

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarPenumbralEclipse(P4, U14);
}

TEST_F(EclipseTest, Year1952) {
  // Dates are TDB Julian Day for 1952-02-11 (or 10 for P1).
  auto P1 = JulianDate(2434053.42282623);  // P1 = 22:08:20 UT
  auto U1 = JulianDate(2434053.50334705);  // U1 = 00:04:17
  auto U4 = JulianDate(2434053.55203917);  // U4 = 01:14:24
  auto P4 = JulianDate(2434053.63249055);  // P4 = 03:10:15

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Dates are TDB Julian Day for 1952-08-05.
  P1 = JulianDate(2434230.22830075);  // P1 = 17:28:13 UT
  U1 = JulianDate(2434230.27385631);  // U1 = 18:33:49
  U4 = JulianDate(2434230.37606695);  // U4 = 21:01:00
  P4 = JulianDate(2434230.42161093);  // P4 = 22:06:35

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14);

  // Later on for additional accuracy: 2 * ArcTan((x_norm_y -
  // y_normx).Norm(),(x_norm_y + y_norm_x).Norm())
  // x_norm_y = x * y.Norm() and y_norm_x = y * x.Norm()

  // Future is not now: check 2048-01-01 Lunar eclipse.
  /*P1 = JulianDate(2469076.66235167);  // 03:52:39 UT
  U1 = JulianDate(2469076.71279148);  // 05:05:17
  U2 = JulianDate(2469076.76776833);  // 06:24:27
  U3 = JulianDate(2469076.80661092);  // 07:20:23
  U4 = JulianDate(2469076.86158778);  // 08:39:33
  P4 = JulianDate(2469076.91195815);  // 09:52:05

  CheckLunarPenumbralEclipse(P1, U14);
  CheckLunarUmbralEclipse(U1, U14);
  CheckLunarUmbralEclipse(U2, U23);
  CheckLunarUmbralEclipse(U3, U23);
  CheckLunarUmbralEclipse(U4, U14);
  CheckLunarPenumbralEclipse(P4, U14); */
}
#endif

}  // namespace physics
}  // namespace principia
<|endoftext|>


<commit_before>/*
 * Run a CGI script.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "cgi_client.hxx"
#include "cgi_quark.h"
#include "cgi_parser.hxx"
#include "pool.hxx"
#include "istream/istream_internal.hxx"
#include "istream/istream_pointer.hxx"
#include "istream/istream_null.hxx"
#include "async.hxx"
#include "header_parser.hxx"
#include "stopwatch.hxx"
#include "strmap.hxx"
#include "http_response.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "util/Cast.hxx"

#include <string.h>
#include <stdlib.h>

struct CGIClient {
    struct istream output;

    struct stopwatch *const stopwatch;

    IstreamPointer input;
    SliceFifoBuffer buffer;

    CGIParser parser;

    /**
     * This flag is true while cgi_parse_headers() is calling
     * http_response_handler_ref::InvokeResponse().  In this case,
     * istream_read(cgi->input) is already up in the stack, and must
     * not be called again.
     */
    bool in_response_callback;

    bool had_input, had_output;

    struct async_operation operation;
    struct http_response_handler_ref handler;

    CGIClient(struct pool &pool, struct stopwatch *_stopwatch,
              struct istream &_input,
              const struct http_response_handler &_handler,
              void *handler_ctx,
              struct async_operation_ref &async_ref);

    /**
     * @return false if the connection has been closed
     */
    bool ReturnResponse();

    /**
     * Feed data into the input buffer and continue parsing response
     * headers from it.  After this function returns, the response may
     * have been delivered to the response handler, and the caller should
     * post the rest of the specified buffer to the response body stream.
     *
     * Caller must hold pool reference.
     *
     * @return the number of bytes consumed from the specified buffer
     * (moved to the input buffer), 0 if the object has been closed
     */
    size_t FeedHeaders(const void *data, size_t length);

    /**
     * Call FeedHeaders() in a loop, to parse as much as possible.
     *
     * Caller must hold pool reference.
     */
    size_t FeedHeadersLoop(const char *data, size_t length);

    /**
     * Caller must hold pool reference.
     */
    size_t FeedHeadersCheck(const char *data, size_t length);

    size_t FeedBody(const char *data, size_t length);

    size_t Feed(const void *data, size_t length);

    void Abort();
};

inline bool
CGIClient::ReturnResponse()
{
    operation.Finished();

    http_status_t status = parser.GetStatus();
    struct strmap &headers = parser.GetHeaders();

    if (http_status_is_empty(status)) {
        /* this response does not have a response body, as indicated
           by the HTTP status code */

        stopwatch_event(stopwatch, "empty");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeResponse(status, &headers, nullptr);
        pool_unref(output.pool);
        return false;
    } else if (parser.IsEOF()) {
        /* the response body is empty */

        stopwatch_event(stopwatch, "empty");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeResponse(status, &headers,
                               istream_null_new(output.pool));
        pool_unref(output.pool);
        return false;
    } else {
        stopwatch_event(stopwatch, "headers");

        in_response_callback = true;
        handler.InvokeResponse(status, &headers, &output);
        in_response_callback = false;
        return input.IsDefined();
    }
}

inline size_t
CGIClient::FeedHeaders(const void *data, size_t length)
{
    assert(!parser.AreHeadersFinished());

    auto w = buffer.Write();
    assert(!w.IsEmpty());

    if (length > w.size)
        length = w.size;

    memcpy(w.data, data, length);
    buffer.Append(length);

    GError *error = nullptr;
    enum completion c = parser.FeedHeaders(*output.pool, buffer, &error);
    switch (c) {
    case C_DONE:
        /* the C_DONE status can only be triggered by new data that
           was just received; therefore, the amount of data still in
           the buffer (= response body) must be smaller */
        assert(buffer.GetAvailable() < length);

        if (!ReturnResponse())
            return 0;

        /* don't consider data still in the buffer (= response body)
           as "consumed"; the caller will attempt to submit it to the
           response body handler */
        return length - buffer.GetAvailable();

    case C_MORE:
        return length;

    case C_ERROR:
        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeAbort(error);
        pool_unref(output.pool);
        return 0;

    case C_CLOSED:
        /* unreachable */
        assert(false);
        return 0;
    }

    /* unreachable */
    assert(false);
    return 0;
}

inline size_t
CGIClient::FeedHeadersLoop(const char *data, size_t length)
{
    assert(length > 0);
    assert(!parser.AreHeadersFinished());

    size_t consumed = 0;

    do {
        size_t nbytes = FeedHeaders(data + consumed, length - consumed);
        if (nbytes == 0)
            break;

        consumed += nbytes;
    } while (consumed < length && !parser.AreHeadersFinished());

    if (!input.IsDefined())
        return 0;

    return consumed;
}

inline size_t
CGIClient::FeedHeadersCheck(const char *data, size_t length)
{
    size_t nbytes = FeedHeadersLoop(data, length);

    assert(nbytes == 0 || input.IsDefined());
    assert(nbytes == 0 ||
           !parser.AreHeadersFinished() ||
           !parser.IsEOF());

    return nbytes;
}

inline size_t
CGIClient::FeedBody(const char *data, size_t length)
{
    if (parser.IsTooMuch(length)) {
        stopwatch_event(stopwatch, "malformed");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "too much data from CGI script");
        istream_deinit_abort(&output, error);
        return 0;
    }

    had_output = true;

    size_t nbytes = istream_invoke_data(&output, data, length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch_event(stopwatch, "end");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        istream_deinit_eof(&output);
        return 0;
    }

    return nbytes;
}

inline size_t
CGIClient::Feed(const void *data, size_t length)
{
    assert(input.IsDefined());

    had_input = true;

    if (!parser.AreHeadersFinished()) {
        const ScopePoolRef ref(*output.pool TRACE_ARGS);

        size_t nbytes = FeedHeadersCheck((const char *)data, length);

        if (nbytes > 0 && nbytes < length &&
            parser.AreHeadersFinished()) {
            /* the headers are finished; now begin sending the
               response body */
            size_t nbytes2 = FeedBody((const char *)data + nbytes,
                                      length - nbytes);
            if (nbytes2 > 0)
                /* more data was consumed */
                nbytes += nbytes2;
            else if (!input.IsDefined())
                /* the connection was closed, must return 0 */
                nbytes = 0;
        }

        return nbytes;
    } else {
        return FeedBody((const char *)data, length);
    }
}

/*
 * input handler
 *
 */

static size_t
cgi_input_data(const void *data, size_t length, void *ctx)
{
    CGIClient *cgi = (CGIClient *)ctx;

    return cgi->Feed(data, length);
}

static ssize_t
cgi_input_direct(FdType type, int fd, size_t max_length, void *ctx)
{
    CGIClient *cgi = (CGIClient *)ctx;

    assert(cgi->parser.AreHeadersFinished());

    cgi->had_input = true;
    cgi->had_output = true;

    if (cgi->parser.KnownLength() &&
        (off_t)max_length > cgi->parser.GetAvailable())
        max_length = (size_t)cgi->parser.GetAvailable();

    ssize_t nbytes = istream_invoke_direct(&cgi->output, type, fd, max_length);
    if (nbytes > 0 && cgi->parser.BodyConsumed(nbytes)) {
        stopwatch_event(cgi->stopwatch, "end");
        stopwatch_dump(cgi->stopwatch);

        cgi->buffer.Free(fb_pool_get());
        cgi->input.Close();
        istream_deinit_eof(&cgi->output);
        return ISTREAM_RESULT_CLOSED;
    }

    return nbytes;
}

static void
cgi_input_eof(void *ctx)
{
    CGIClient *cgi = (CGIClient *)ctx;

    cgi->input.Clear();

    if (!cgi->parser.AreHeadersFinished()) {
        stopwatch_event(cgi->stopwatch, "malformed");
        stopwatch_dump(cgi->stopwatch);

        assert(!istream_has_handler(&cgi->output));

        cgi->buffer.Free(fb_pool_get());

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "premature end of headers from CGI script");
        cgi->handler.InvokeAbort(error);
        pool_unref(cgi->output.pool);
    } else if (cgi->parser.DoesRequireMore()) {
        stopwatch_event(cgi->stopwatch, "malformed");
        stopwatch_dump(cgi->stopwatch);

        cgi->buffer.Free(fb_pool_get());

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "premature end of response body from CGI script");
        istream_deinit_abort(&cgi->output, error);
    } else {
        stopwatch_event(cgi->stopwatch, "end");
        stopwatch_dump(cgi->stopwatch);

        cgi->buffer.Free(fb_pool_get());
        istream_deinit_eof(&cgi->output);
    }
}

static void
cgi_input_abort(GError *error, void *ctx)
{
    CGIClient *cgi = (CGIClient *)ctx;

    stopwatch_event(cgi->stopwatch, "abort");
    stopwatch_dump(cgi->stopwatch);

    cgi->input.Clear();

    if (!cgi->parser.AreHeadersFinished()) {
        /* the response hasn't been sent yet: notify the response
           handler */
        assert(!istream_has_handler(&cgi->output));

        cgi->buffer.Free(fb_pool_get());

        g_prefix_error(&error, "CGI request body failed: ");
        cgi->handler.InvokeAbort(error);
        pool_unref(cgi->output.pool);
    } else {
        /* response has been sent: abort only the output stream */
        cgi->buffer.Free(fb_pool_get());
        istream_deinit_abort(&cgi->output, error);
    }
}

static const struct istream_handler cgi_input_handler = {
    .data = cgi_input_data,
    .direct = cgi_input_direct,
    .eof = cgi_input_eof,
    .abort = cgi_input_abort,
};

/*
 * istream implementation
 *
 */

static inline CGIClient *
istream_to_cgi(struct istream *istream)
{
    return &ContainerCast2(*istream, &CGIClient::output);
}

static off_t
istream_cgi_available(struct istream *istream, bool partial)
{
    CGIClient *cgi = istream_to_cgi(istream);

    if (cgi->parser.KnownLength())
        return cgi->parser.GetAvailable();

    if (!cgi->input.IsDefined())
        return 0;

    if (cgi->in_response_callback)
        /* this condition catches the case in cgi_parse_headers():
           http_response_handler_ref::InvokeResponse() might
           recursively call istream_read(cgi->input) */
        return (off_t)-1;

    return cgi->input.GetAvailable(partial);
}

static void
istream_cgi_read(struct istream *istream)
{
    CGIClient *cgi = istream_to_cgi(istream);

    if (cgi->input.IsDefined()) {
        cgi->input.SetDirect(cgi->output.handler_direct);

        /* this condition catches the case in cgi_parse_headers():
           http_response_handler_ref::InvokeResponse() might
           recursively call istream_read(cgi->input) */
        if (cgi->in_response_callback) {
            return;
        }

        const ScopePoolRef ref(*cgi->output.pool TRACE_ARGS);

        cgi->had_output = false;
        do {
            cgi->had_input = false;
            cgi->input.Read();
        } while (cgi->input.IsDefined() && cgi->had_input &&
                 !cgi->had_output);
    }
}

static void
istream_cgi_close(struct istream *istream)
{
    CGIClient *cgi = istream_to_cgi(istream);

    cgi->buffer.Free(fb_pool_get());

    if (cgi->input.IsDefined())
        cgi->input.ClearAndClose();

    istream_deinit(&cgi->output);
}

static const struct istream_class istream_cgi = {
    .available = istream_cgi_available,
    .skip = nullptr,
    .read = istream_cgi_read,
    .as_fd = nullptr,
    .close = istream_cgi_close,
};

/*
 * async operation
 *
 */

inline void
CGIClient::Abort()
{
    assert(input.IsDefined());

    buffer.Free(fb_pool_get());
    input.Close();
    pool_unref(output.pool);
}

/*
 * constructor
 *
 */

inline
CGIClient::CGIClient(struct pool &pool, struct stopwatch *_stopwatch,
                     struct istream &_input,
                     const struct http_response_handler &_handler,
                     void *handler_ctx,
                     struct async_operation_ref &async_ref)
    :stopwatch(_stopwatch),
     input(_input, cgi_input_handler, this),
     buffer(fb_pool_get()),
     parser(pool)
{
    istream_init(&output, &istream_cgi, &pool);

    handler.Set(_handler, handler_ctx);

    operation.Init2<CGIClient>();
    async_ref.Set(operation);

    input.Read();
}

void
cgi_client_new(struct pool *pool, struct stopwatch *stopwatch,
               struct istream *input,
               const struct http_response_handler *handler, void *handler_ctx,
               struct async_operation_ref *async_ref)
{
    assert(pool != nullptr);
    assert(input != nullptr);
    assert(handler != nullptr);

    NewFromPool<CGIClient>(*pool, *pool, stopwatch, *input,
                           *handler, handler_ctx, *async_ref);
}
<commit_msg>cgi/cgi_client: use MakeIstreamHandler<commit_after>/*
 * Run a CGI script.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "cgi_client.hxx"
#include "cgi_quark.h"
#include "cgi_parser.hxx"
#include "pool.hxx"
#include "istream/istream_oo.hxx"
#include "istream/istream_pointer.hxx"
#include "istream/istream_null.hxx"
#include "async.hxx"
#include "header_parser.hxx"
#include "stopwatch.hxx"
#include "strmap.hxx"
#include "http_response.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "util/Cast.hxx"

#include <string.h>
#include <stdlib.h>

struct CGIClient {
    struct istream output;

    struct stopwatch *const stopwatch;

    IstreamPointer input;
    SliceFifoBuffer buffer;

    CGIParser parser;

    /**
     * This flag is true while cgi_parse_headers() is calling
     * http_response_handler_ref::InvokeResponse().  In this case,
     * istream_read(cgi->input) is already up in the stack, and must
     * not be called again.
     */
    bool in_response_callback;

    bool had_input, had_output;

    struct async_operation operation;
    struct http_response_handler_ref handler;

    CGIClient(struct pool &pool, struct stopwatch *_stopwatch,
              struct istream &_input,
              const struct http_response_handler &_handler,
              void *handler_ctx,
              struct async_operation_ref &async_ref);

    /**
     * @return false if the connection has been closed
     */
    bool ReturnResponse();

    /**
     * Feed data into the input buffer and continue parsing response
     * headers from it.  After this function returns, the response may
     * have been delivered to the response handler, and the caller should
     * post the rest of the specified buffer to the response body stream.
     *
     * Caller must hold pool reference.
     *
     * @return the number of bytes consumed from the specified buffer
     * (moved to the input buffer), 0 if the object has been closed
     */
    size_t FeedHeaders(const void *data, size_t length);

    /**
     * Call FeedHeaders() in a loop, to parse as much as possible.
     *
     * Caller must hold pool reference.
     */
    size_t FeedHeadersLoop(const char *data, size_t length);

    /**
     * Caller must hold pool reference.
     */
    size_t FeedHeadersCheck(const char *data, size_t length);

    size_t FeedBody(const char *data, size_t length);

    void Abort();

    /* istream handler */
    size_t OnData(const void *data, size_t length);
    ssize_t OnDirect(FdType type, int fd, size_t max_length);
    void OnEof();
    void OnError(GError *error);
};

inline bool
CGIClient::ReturnResponse()
{
    operation.Finished();

    http_status_t status = parser.GetStatus();
    struct strmap &headers = parser.GetHeaders();

    if (http_status_is_empty(status)) {
        /* this response does not have a response body, as indicated
           by the HTTP status code */

        stopwatch_event(stopwatch, "empty");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeResponse(status, &headers, nullptr);
        pool_unref(output.pool);
        return false;
    } else if (parser.IsEOF()) {
        /* the response body is empty */

        stopwatch_event(stopwatch, "empty");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeResponse(status, &headers,
                               istream_null_new(output.pool));
        pool_unref(output.pool);
        return false;
    } else {
        stopwatch_event(stopwatch, "headers");

        in_response_callback = true;
        handler.InvokeResponse(status, &headers, &output);
        in_response_callback = false;
        return input.IsDefined();
    }
}

inline size_t
CGIClient::FeedHeaders(const void *data, size_t length)
{
    assert(!parser.AreHeadersFinished());

    auto w = buffer.Write();
    assert(!w.IsEmpty());

    if (length > w.size)
        length = w.size;

    memcpy(w.data, data, length);
    buffer.Append(length);

    GError *error = nullptr;
    enum completion c = parser.FeedHeaders(*output.pool, buffer, &error);
    switch (c) {
    case C_DONE:
        /* the C_DONE status can only be triggered by new data that
           was just received; therefore, the amount of data still in
           the buffer (= response body) must be smaller */
        assert(buffer.GetAvailable() < length);

        if (!ReturnResponse())
            return 0;

        /* don't consider data still in the buffer (= response body)
           as "consumed"; the caller will attempt to submit it to the
           response body handler */
        return length - buffer.GetAvailable();

    case C_MORE:
        return length;

    case C_ERROR:
        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        handler.InvokeAbort(error);
        pool_unref(output.pool);
        return 0;

    case C_CLOSED:
        /* unreachable */
        assert(false);
        return 0;
    }

    /* unreachable */
    assert(false);
    return 0;
}

inline size_t
CGIClient::FeedHeadersLoop(const char *data, size_t length)
{
    assert(length > 0);
    assert(!parser.AreHeadersFinished());

    size_t consumed = 0;

    do {
        size_t nbytes = FeedHeaders(data + consumed, length - consumed);
        if (nbytes == 0)
            break;

        consumed += nbytes;
    } while (consumed < length && !parser.AreHeadersFinished());

    if (!input.IsDefined())
        return 0;

    return consumed;
}

inline size_t
CGIClient::FeedHeadersCheck(const char *data, size_t length)
{
    size_t nbytes = FeedHeadersLoop(data, length);

    assert(nbytes == 0 || input.IsDefined());
    assert(nbytes == 0 ||
           !parser.AreHeadersFinished() ||
           !parser.IsEOF());

    return nbytes;
}

inline size_t
CGIClient::FeedBody(const char *data, size_t length)
{
    if (parser.IsTooMuch(length)) {
        stopwatch_event(stopwatch, "malformed");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "too much data from CGI script");
        istream_deinit_abort(&output, error);
        return 0;
    }

    had_output = true;

    size_t nbytes = istream_invoke_data(&output, data, length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch_event(stopwatch, "end");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.ClearAndClose();
        istream_deinit_eof(&output);
        return 0;
    }

    return nbytes;
}

/*
 * input handler
 *
 */

inline size_t
CGIClient::OnData(const void *data, size_t length)
{
    assert(input.IsDefined());

    had_input = true;

    if (!parser.AreHeadersFinished()) {
        const ScopePoolRef ref(*output.pool TRACE_ARGS);

        size_t nbytes = FeedHeadersCheck((const char *)data, length);

        if (nbytes > 0 && nbytes < length &&
            parser.AreHeadersFinished()) {
            /* the headers are finished; now begin sending the
               response body */
            size_t nbytes2 = FeedBody((const char *)data + nbytes,
                                      length - nbytes);
            if (nbytes2 > 0)
                /* more data was consumed */
                nbytes += nbytes2;
            else if (!input.IsDefined())
                /* the connection was closed, must return 0 */
                nbytes = 0;
        }

        return nbytes;
    } else {
        return FeedBody((const char *)data, length);
    }
}

inline ssize_t
CGIClient::OnDirect(FdType type, int fd, size_t max_length)
{
    assert(parser.AreHeadersFinished());

    had_input = true;
    had_output = true;

    if (parser.KnownLength() &&
        (off_t)max_length > parser.GetAvailable())
        max_length = (size_t)parser.GetAvailable();

    ssize_t nbytes = istream_invoke_direct(&output, type, fd, max_length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch_event(stopwatch, "end");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        input.Close();
        istream_deinit_eof(&output);
        return ISTREAM_RESULT_CLOSED;
    }

    return nbytes;
}

inline void
CGIClient::OnEof()
{
    input.Clear();

    if (!parser.AreHeadersFinished()) {
        stopwatch_event(stopwatch, "malformed");
        stopwatch_dump(stopwatch);

        assert(!istream_has_handler(&output));

        buffer.Free(fb_pool_get());

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "premature end of headers from CGI script");
        handler.InvokeAbort(error);
        pool_unref(output.pool);
    } else if (parser.DoesRequireMore()) {
        stopwatch_event(stopwatch, "malformed");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());

        GError *error =
            g_error_new_literal(cgi_quark(), 0,
                                "premature end of response body from CGI script");
        istream_deinit_abort(&output, error);
    } else {
        stopwatch_event(stopwatch, "end");
        stopwatch_dump(stopwatch);

        buffer.Free(fb_pool_get());
        istream_deinit_eof(&output);
    }
}

inline void
CGIClient::OnError(GError *error)
{
    stopwatch_event(stopwatch, "abort");
    stopwatch_dump(stopwatch);

    input.Clear();

    if (!parser.AreHeadersFinished()) {
        /* the response hasn't been sent yet: notify the response
           handler */
        assert(!istream_has_handler(&output));

        buffer.Free(fb_pool_get());

        g_prefix_error(&error, "CGI request body failed: ");
        handler.InvokeAbort(error);
        pool_unref(output.pool);
    } else {
        /* response has been sent: abort only the output stream */
        buffer.Free(fb_pool_get());
        istream_deinit_abort(&output, error);
    }
}

/*
 * istream implementation
 *
 */

static inline CGIClient *
istream_to_cgi(struct istream *istream)
{
    return &ContainerCast2(*istream, &CGIClient::output);
}

static off_t
istream_cgi_available(struct istream *istream, bool partial)
{
    CGIClient *cgi = istream_to_cgi(istream);

    if (cgi->parser.KnownLength())
        return cgi->parser.GetAvailable();

    if (!cgi->input.IsDefined())
        return 0;

    if (cgi->in_response_callback)
        /* this condition catches the case in cgi_parse_headers():
           http_response_handler_ref::InvokeResponse() might
           recursively call istream_read(cgi->input) */
        return (off_t)-1;

    return cgi->input.GetAvailable(partial);
}

static void
istream_cgi_read(struct istream *istream)
{
    CGIClient *cgi = istream_to_cgi(istream);

    if (cgi->input.IsDefined()) {
        cgi->input.SetDirect(cgi->output.handler_direct);

        /* this condition catches the case in cgi_parse_headers():
           http_response_handler_ref::InvokeResponse() might
           recursively call istream_read(cgi->input) */
        if (cgi->in_response_callback) {
            return;
        }

        const ScopePoolRef ref(*cgi->output.pool TRACE_ARGS);

        cgi->had_output = false;
        do {
            cgi->had_input = false;
            cgi->input.Read();
        } while (cgi->input.IsDefined() && cgi->had_input &&
                 !cgi->had_output);
    }
}

static void
istream_cgi_close(struct istream *istream)
{
    CGIClient *cgi = istream_to_cgi(istream);

    cgi->buffer.Free(fb_pool_get());

    if (cgi->input.IsDefined())
        cgi->input.ClearAndClose();

    istream_deinit(&cgi->output);
}

static const struct istream_class istream_cgi = {
    .available = istream_cgi_available,
    .skip = nullptr,
    .read = istream_cgi_read,
    .as_fd = nullptr,
    .close = istream_cgi_close,
};

/*
 * async operation
 *
 */

inline void
CGIClient::Abort()
{
    assert(input.IsDefined());

    buffer.Free(fb_pool_get());
    input.Close();
    pool_unref(output.pool);
}

/*
 * constructor
 *
 */

inline
CGIClient::CGIClient(struct pool &pool, struct stopwatch *_stopwatch,
                     struct istream &_input,
                     const struct http_response_handler &_handler,
                     void *handler_ctx,
                     struct async_operation_ref &async_ref)
    :stopwatch(_stopwatch),
     input(_input, MakeIstreamHandler<CGIClient>::handler, this),
     buffer(fb_pool_get()),
     parser(pool)
{
    istream_init(&output, &istream_cgi, &pool);

    handler.Set(_handler, handler_ctx);

    operation.Init2<CGIClient>();
    async_ref.Set(operation);

    input.Read();
}

void
cgi_client_new(struct pool *pool, struct stopwatch *stopwatch,
               struct istream *input,
               const struct http_response_handler *handler, void *handler_ctx,
               struct async_operation_ref *async_ref)
{
    assert(pool != nullptr);
    assert(input != nullptr);
    assert(handler != nullptr);

    NewFromPool<CGIClient>(*pool, *pool, stopwatch, *input,
                           *handler, handler_ctx, *async_ref);
}
<|endoftext|>


<commit_before>/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Alex Beregszaszi
 * @date 2016
 * Standard JSON compiler interface.
 */

#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/SourceReferenceFormatter.h>
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>

using namespace std;
using namespace dev;
using namespace dev::solidity;

namespace {

Json::Value formatError(
	bool _warning,
	string const& _type,
	string const& _component,
	string const& _message,
	string const& _formattedMessage = "",
	Json::Value const& _sourceLocation = Json::Value()
)
{
	Json::Value error = Json::objectValue;
	error["type"] = _type;
	error["component"] = _component;
	error["severity"] = _warning ? "warning" : "error";
	error["message"] = _message;
	error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
	if (_sourceLocation.isObject())
		error["sourceLocation"] = _sourceLocation;
	return error;
}

Json::Value formatFatalError(string const& _type, string const& _message)
{
	Json::Value output = Json::objectValue;
	output["errors"] = Json::arrayValue;
	output["errors"].append(formatError(false, _type, "general", _message));
	return output;
}

Json::Value formatErrorWithException(
	Exception const& _exception,
	bool const& _warning,
	string const& _type,
	string const& _component,
	string const& _message,
	function<Scanner const&(string const&)> const& _scannerFromSourceName
)
{
	string message;
	string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _message, _scannerFromSourceName);

	// NOTE: the below is partially a copy from SourceReferenceFormatter
	SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);

	if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
		message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
	else
		message = _message;

	if (location && location->sourceName)
	{
		Json::Value sourceLocation = Json::objectValue;
		sourceLocation["file"] = *location->sourceName;
		sourceLocation["start"] = location->start;
		sourceLocation["end"] = location->end;
	}

	return formatError(_warning, _type, _component, message, formattedMessage, location);
}

StringMap createSourceList(Json::Value const& _input)
{
	StringMap sources;
	Json::Value const& jsonSources = _input["sources"];
	if (jsonSources.isObject())
		for (auto const& sourceName: jsonSources.getMemberNames())
			sources[sourceName] = jsonSources[sourceName]["content"].asString();
	return sources;
}

Json::Value methodIdentifiers(ContractDefinition const& _contract)
{
	Json::Value methodIdentifiers(Json::objectValue);
	for (auto const& it: _contract.interfaceFunctions())
		methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
	return methodIdentifiers;
}

Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
	Json::Value ret(Json::objectValue);

	for (auto const& ref: linkReferences)
	{
		string const& fullname = ref.second;
		size_t colon = fullname.find(':');
		solAssert(colon != string::npos, "");
		string file = fullname.substr(0, colon);
		string name = fullname.substr(colon + 1);

		Json::Value fileObject = ret.get(file, Json::objectValue);
		Json::Value libraryArray = fileObject.get(name, Json::arrayValue);

		Json::Value entry = Json::objectValue;
		entry["start"] = Json::UInt(ref.first);
		entry["length"] = 20;

		libraryArray.append(entry);
		fileObject[name] = libraryArray;
		ret[file] = fileObject;
	}

	return ret;
}

}

Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
	m_compilerStack.reset(false);

	Json::Value const& sources = _input["sources"];
	if (!sources)
		return formatFatalError("JSONError", "No input sources specified.");

	for (auto const& sourceName: sources.getMemberNames())
		if (sources[sourceName]["content"].isString())
			m_compilerStack.addSource(sourceName, sources[sourceName]["content"].asString());
		else if (sources[sourceName]["urls"].isArray())
			return formatFatalError("UnimplementedFeatureError", "Input URLs not supported yet.");
		else
			return formatFatalError("JSONError", "Invalid input source specified.");

	Json::Value const& settings = _input.get("settings", Json::Value());

	vector<string> remappings;
	for (auto const& remapping: settings.get("remappings", Json::Value()))
		remappings.push_back(remapping.asString());
	m_compilerStack.setRemappings(remappings);

	Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
	bool optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
	unsigned optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();

	map<string, h160> libraries;
	Json::Value jsonLibraries = settings.get("libraries", Json::Value());
	for (auto const& sourceName: jsonLibraries.getMemberNames())
	{
		auto const& jsonSourceName = jsonLibraries[sourceName];
		for (auto const& library: jsonSourceName.getMemberNames())
			// @TODO use libraries only for the given source
			libraries[library] = h160(jsonSourceName[library].asString());
	}

	Json::Value metadataSettings = settings.get("metadata", Json::Value());
	m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());

	auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); };

	Json::Value errors = Json::arrayValue;
	bool success = false;

	try
	{
		success = m_compilerStack.compile(optimize, optimizeRuns, libraries);

		for (auto const& error: m_compilerStack.errors())
		{
			auto err = dynamic_pointer_cast<Error const>(error);

			errors.append(formatErrorWithException(
				*error,
				err->type() == Error::Type::Warning,
				err->typeName(),
				"general",
				"",
				scannerFromSourceName
			));
		}
	}
	catch (Error const& _error)
	{
		if (_error.type() == Error::Type::DocstringParsingError)
			errors.append(formatError(
				false,
				"DocstringParsingError",
				"general",
				"Documentation parsing error: " + *boost::get_error_info<errinfo_comment>(_error)
			));
		else
			errors.append(formatErrorWithException(
				_error,
				false,
				_error.typeName(),
				"general",
				"",
				scannerFromSourceName
			));
	}
	catch (CompilerError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"CompilerError",
			"general",
			"Compiler error (" + _exception.lineInfo() + ")",
			scannerFromSourceName
		));
	}
	catch (InternalCompilerError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"InternalCompilerError",
			"general",
			"Internal compiler error (" + _exception.lineInfo() + ")", scannerFromSourceName
		));
	}
	catch (UnimplementedFeatureError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"UnimplementedFeatureError",
			"general",
			"Unimplemented feature (" + _exception.lineInfo() + ")",
			scannerFromSourceName));
	}
	catch (Exception const& _exception)
	{
		errors.append(formatError(
			false,
			"Exception",
			"general",
			"Exception during compilation: " + boost::diagnostic_information(_exception)
		));
	}
	catch (...)
	{
		errors.append(formatError(
			false,
			"Exception",
			"general",
			"Unknown exception during compilation."
		));
	}

	Json::Value output = Json::objectValue;

	if (errors.size() > 0)
		output["errors"] = errors;

	/// Inconsistent state - stop here to receive error reports from users
	if (!success && (errors.size() == 0))
		return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");

	output["sources"] = Json::objectValue;
	unsigned sourceIndex = 0;
	for (auto const& source: m_compilerStack.sourceNames())
	{
		Json::Value sourceResult = Json::objectValue;
		sourceResult["id"] = sourceIndex++;
		sourceResult["ast"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json();
		output["sources"][source] = sourceResult;
	}

	Json::Value contractsOutput = Json::objectValue;
	for (string const& contractName: m_compilerStack.contractNames())
	{
		size_t colon = contractName.find(':');
		solAssert(colon != string::npos, "");
		string file = contractName.substr(0, colon);
		string name = contractName.substr(colon + 1);

		// ABI, documentation and metadata
		Json::Value contractData(Json::objectValue);
		contractData["abi"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::ABIInterface));
		contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
		contractData["userdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecUser));
		contractData["devdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecDev));

		// EVM
		Json::Value evmData(Json::objectValue);
		// @TODO: add ir
		ostringstream tmp;
		m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
		evmData["assembly"] = tmp.str();
		evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
		evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
		evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);

		// EVM bytecode
		Json::Value bytecode(Json::objectValue);
		bytecode["object"] = m_compilerStack.object(contractName).toHex();
		bytecode["opcodes"] = solidity::disassemble(m_compilerStack.object(contractName).bytecode);
		auto sourceMap = m_compilerStack.sourceMapping(contractName);
		bytecode["sourceMap"] = sourceMap ? *sourceMap : "";
		bytecode["linkReferences"] = formatLinkReferences(m_compilerStack.object(contractName).linkReferences);
		evmData["bytecode"] = bytecode;

		// EVM deployed bytecode
		Json::Value deployedBytecode(Json::objectValue);
		deployedBytecode["object"] = m_compilerStack.runtimeObject(contractName).toHex();
		deployedBytecode["opcodes"] = solidity::disassemble(m_compilerStack.runtimeObject(contractName).bytecode);
		auto runtimeSourceMap = m_compilerStack.runtimeSourceMapping(contractName);
		deployedBytecode["sourceMap"] = runtimeSourceMap ? *runtimeSourceMap : "";
		deployedBytecode["linkReferences"] = formatLinkReferences(m_compilerStack.runtimeObject(contractName).linkReferences);
		evmData["deployedBytecode"] = deployedBytecode;

		contractData["evm"] = evmData;

		if (!contractsOutput.isMember(file))
			contractsOutput[file] = Json::objectValue;

		contractsOutput[file][name] = contractData;
	}
	output["contracts"] = contractsOutput;

	{
		ErrorList formalErrors;
		if (m_compilerStack.prepareFormalAnalysis(&formalErrors))
			output["why3"] = m_compilerStack.formalTranslation();

		for (auto const& error: formalErrors)
		{
			auto err = dynamic_pointer_cast<Error const>(error);

			errors.append(formatErrorWithException(
				*error,
				err->type() == Error::Type::Warning,
				err->typeName(),
				"general",
				"",
				scannerFromSourceName
			));
		}

		// FIXME!!
		if (!formalErrors.empty())
			output["errors"] = errors;
	}

	return output;
}

Json::Value StandardCompiler::compile(Json::Value const& _input)
{
	try
	{
		return compileInternal(_input);
	}
	catch (...)
	{
		return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compilerInternal");
	}
}

string StandardCompiler::compile(string const& _input)
{
	Json::Value input;
	Json::Reader reader;

	try
	{
		if (!reader.parse(_input, input, false))
			return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages()));
	}
	catch(...)
	{
		return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
	}

	// cout << "Input: " << input.toStyledString() << endl;
	Json::Value output = compile(input);
	// cout << "Output: " << output.toStyledString() << endl;

	try
	{
		return jsonCompactPrint(output);
	}
	catch(...)
	{
		return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
	}
}
<commit_msg>Pull out collectEVMObject<commit_after>/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Alex Beregszaszi
 * @date 2016
 * Standard JSON compiler interface.
 */

#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/SourceReferenceFormatter.h>
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>

using namespace std;
using namespace dev;
using namespace dev::solidity;

namespace {

Json::Value formatError(
	bool _warning,
	string const& _type,
	string const& _component,
	string const& _message,
	string const& _formattedMessage = "",
	Json::Value const& _sourceLocation = Json::Value()
)
{
	Json::Value error = Json::objectValue;
	error["type"] = _type;
	error["component"] = _component;
	error["severity"] = _warning ? "warning" : "error";
	error["message"] = _message;
	error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
	if (_sourceLocation.isObject())
		error["sourceLocation"] = _sourceLocation;
	return error;
}

Json::Value formatFatalError(string const& _type, string const& _message)
{
	Json::Value output = Json::objectValue;
	output["errors"] = Json::arrayValue;
	output["errors"].append(formatError(false, _type, "general", _message));
	return output;
}

Json::Value formatErrorWithException(
	Exception const& _exception,
	bool const& _warning,
	string const& _type,
	string const& _component,
	string const& _message,
	function<Scanner const&(string const&)> const& _scannerFromSourceName
)
{
	string message;
	string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _message, _scannerFromSourceName);

	// NOTE: the below is partially a copy from SourceReferenceFormatter
	SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);

	if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
		message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
	else
		message = _message;

	if (location && location->sourceName)
	{
		Json::Value sourceLocation = Json::objectValue;
		sourceLocation["file"] = *location->sourceName;
		sourceLocation["start"] = location->start;
		sourceLocation["end"] = location->end;
	}

	return formatError(_warning, _type, _component, message, formattedMessage, location);
}

StringMap createSourceList(Json::Value const& _input)
{
	StringMap sources;
	Json::Value const& jsonSources = _input["sources"];
	if (jsonSources.isObject())
		for (auto const& sourceName: jsonSources.getMemberNames())
			sources[sourceName] = jsonSources[sourceName]["content"].asString();
	return sources;
}

Json::Value methodIdentifiers(ContractDefinition const& _contract)
{
	Json::Value methodIdentifiers(Json::objectValue);
	for (auto const& it: _contract.interfaceFunctions())
		methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
	return methodIdentifiers;
}

Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
	Json::Value ret(Json::objectValue);

	for (auto const& ref: linkReferences)
	{
		string const& fullname = ref.second;
		size_t colon = fullname.find(':');
		solAssert(colon != string::npos, "");
		string file = fullname.substr(0, colon);
		string name = fullname.substr(colon + 1);

		Json::Value fileObject = ret.get(file, Json::objectValue);
		Json::Value libraryArray = fileObject.get(name, Json::arrayValue);

		Json::Value entry = Json::objectValue;
		entry["start"] = Json::UInt(ref.first);
		entry["length"] = 20;

		libraryArray.append(entry);
		fileObject[name] = libraryArray;
		ret[file] = fileObject;
	}

	return ret;
}

Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _sourceMap)
{
	Json::Value output = Json::objectValue;
	output["object"] = _object.toHex();
	output["opcodes"] = solidity::disassemble(_object.bytecode);
	output["sourceMap"] = _sourceMap ? *_sourceMap : "";
	output["linkReferences"] = formatLinkReferences(_object.linkReferences);
	return output;
}

}

Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
	m_compilerStack.reset(false);

	Json::Value const& sources = _input["sources"];
	if (!sources)
		return formatFatalError("JSONError", "No input sources specified.");

	for (auto const& sourceName: sources.getMemberNames())
		if (sources[sourceName]["content"].isString())
			m_compilerStack.addSource(sourceName, sources[sourceName]["content"].asString());
		else if (sources[sourceName]["urls"].isArray())
			return formatFatalError("UnimplementedFeatureError", "Input URLs not supported yet.");
		else
			return formatFatalError("JSONError", "Invalid input source specified.");

	Json::Value const& settings = _input.get("settings", Json::Value());

	vector<string> remappings;
	for (auto const& remapping: settings.get("remappings", Json::Value()))
		remappings.push_back(remapping.asString());
	m_compilerStack.setRemappings(remappings);

	Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
	bool optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
	unsigned optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();

	map<string, h160> libraries;
	Json::Value jsonLibraries = settings.get("libraries", Json::Value());
	for (auto const& sourceName: jsonLibraries.getMemberNames())
	{
		auto const& jsonSourceName = jsonLibraries[sourceName];
		for (auto const& library: jsonSourceName.getMemberNames())
			// @TODO use libraries only for the given source
			libraries[library] = h160(jsonSourceName[library].asString());
	}

	Json::Value metadataSettings = settings.get("metadata", Json::Value());
	m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());

	auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); };

	Json::Value errors = Json::arrayValue;
	bool success = false;

	try
	{
		success = m_compilerStack.compile(optimize, optimizeRuns, libraries);

		for (auto const& error: m_compilerStack.errors())
		{
			auto err = dynamic_pointer_cast<Error const>(error);

			errors.append(formatErrorWithException(
				*error,
				err->type() == Error::Type::Warning,
				err->typeName(),
				"general",
				"",
				scannerFromSourceName
			));
		}
	}
	catch (Error const& _error)
	{
		if (_error.type() == Error::Type::DocstringParsingError)
			errors.append(formatError(
				false,
				"DocstringParsingError",
				"general",
				"Documentation parsing error: " + *boost::get_error_info<errinfo_comment>(_error)
			));
		else
			errors.append(formatErrorWithException(
				_error,
				false,
				_error.typeName(),
				"general",
				"",
				scannerFromSourceName
			));
	}
	catch (CompilerError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"CompilerError",
			"general",
			"Compiler error (" + _exception.lineInfo() + ")",
			scannerFromSourceName
		));
	}
	catch (InternalCompilerError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"InternalCompilerError",
			"general",
			"Internal compiler error (" + _exception.lineInfo() + ")", scannerFromSourceName
		));
	}
	catch (UnimplementedFeatureError const& _exception)
	{
		errors.append(formatErrorWithException(
			_exception,
			false,
			"UnimplementedFeatureError",
			"general",
			"Unimplemented feature (" + _exception.lineInfo() + ")",
			scannerFromSourceName));
	}
	catch (Exception const& _exception)
	{
		errors.append(formatError(
			false,
			"Exception",
			"general",
			"Exception during compilation: " + boost::diagnostic_information(_exception)
		));
	}
	catch (...)
	{
		errors.append(formatError(
			false,
			"Exception",
			"general",
			"Unknown exception during compilation."
		));
	}

	Json::Value output = Json::objectValue;

	if (errors.size() > 0)
		output["errors"] = errors;

	/// Inconsistent state - stop here to receive error reports from users
	if (!success && (errors.size() == 0))
		return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");

	output["sources"] = Json::objectValue;
	unsigned sourceIndex = 0;
	for (auto const& source: m_compilerStack.sourceNames())
	{
		Json::Value sourceResult = Json::objectValue;
		sourceResult["id"] = sourceIndex++;
		sourceResult["ast"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json();
		output["sources"][source] = sourceResult;
	}

	Json::Value contractsOutput = Json::objectValue;
	for (string const& contractName: m_compilerStack.contractNames())
	{
		size_t colon = contractName.find(':');
		solAssert(colon != string::npos, "");
		string file = contractName.substr(0, colon);
		string name = contractName.substr(colon + 1);

		// ABI, documentation and metadata
		Json::Value contractData(Json::objectValue);
		contractData["abi"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::ABIInterface));
		contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
		contractData["userdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecUser));
		contractData["devdoc"] = dev::jsonCompactPrint(m_compilerStack.metadata(contractName, DocumentationType::NatspecDev));

		// EVM
		Json::Value evmData(Json::objectValue);
		// @TODO: add ir
		ostringstream tmp;
		m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
		evmData["assembly"] = tmp.str();
		evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
		evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
		evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);

		evmData["bytecode"] = collectEVMObject(
			m_compilerStack.object(contractName),
			m_compilerStack.sourceMapping(contractName)
		);

		evmData["deployedBytecode"] = collectEVMObject(
			m_compilerStack.runtimeObject(contractName),
			m_compilerStack.runtimeSourceMapping(contractName)
		);

		contractData["evm"] = evmData;

		if (!contractsOutput.isMember(file))
			contractsOutput[file] = Json::objectValue;

		contractsOutput[file][name] = contractData;
	}
	output["contracts"] = contractsOutput;

	{
		ErrorList formalErrors;
		if (m_compilerStack.prepareFormalAnalysis(&formalErrors))
			output["why3"] = m_compilerStack.formalTranslation();

		for (auto const& error: formalErrors)
		{
			auto err = dynamic_pointer_cast<Error const>(error);

			errors.append(formatErrorWithException(
				*error,
				err->type() == Error::Type::Warning,
				err->typeName(),
				"general",
				"",
				scannerFromSourceName
			));
		}

		// FIXME!!
		if (!formalErrors.empty())
			output["errors"] = errors;
	}

	return output;
}

Json::Value StandardCompiler::compile(Json::Value const& _input)
{
	try
	{
		return compileInternal(_input);
	}
	catch (...)
	{
		return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compilerInternal");
	}
}

string StandardCompiler::compile(string const& _input)
{
	Json::Value input;
	Json::Reader reader;

	try
	{
		if (!reader.parse(_input, input, false))
			return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages()));
	}
	catch(...)
	{
		return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
	}

	// cout << "Input: " << input.toStyledString() << endl;
	Json::Value output = compile(input);
	// cout << "Output: " << output.toStyledString() << endl;

	try
	{
		return jsonCompactPrint(output);
	}
	catch(...)
	{
		return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
	}
}
<|endoftext|>


<commit_before>#include "lexer.hpp"

#include <cctype>
#include <algorithm>
#include <vector>

namespace klang {

Token::Token()
  : type_(TokenType::UNKNOWN), str_(), line_(-1)
{}

Token::Token(TokenType type, const std::string& str, int line)
  : type_(type), str_(str), line_(line)
{}

TokenType Token::type() const { return type_; }
std::string Token::str() const { return str_; }
int Token::line() const { return line_; }

bool alphabet(char c) {
  return std::isalpha(c);
}

bool alphabet_or_bar(char c) {
  return (c == '_' || alphabet(c));
}

bool nonzero_digit(char c) {
  return (c >= '1' && c <= '9');
}

bool decimal_digit(char c) {
  return std::isdigit(c);
}

bool identifier(const std::string& str) {
  if (str.empty() || !alphabet_or_bar(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!alphabet_or_bar(c) && !decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool decimal_integer(const std::string& str) {
  if (str == "0") return true;
  if (str.empty() || !nonzero_digit(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool symbol(const std::string& str) {
  using std::begin;
  using std::end;
  static std::vector<std::string> const symbol_list = {
    "~", "+", "-", "*", "/", "%",
    ":=", ":+=", ":-=", ":*=", ":/=", ":%=",
    "=", "=/", "<", ">", "<=", ">=",
    ";", "(", ")", "{", "}", "->", "~}",
    "and", "or", "not", "int", "def", "var",
    "if", "else", "while", "for", "break", "continue", "return"
  };
  return (std::find(begin(symbol_list), end(symbol_list), str) != end(symbol_list));
}

bool ignore(const std::string& str) {
  if(str.size() != 1) return false;
  return std::isspace(str[0]);
}

bool singleline_comment(const std::string& str) {
  bool inside = (str.back() == '\n' || str.find("\n") == std::string::npos);
  return (str.compare(0, 2, "~~") == 0 && inside);
}

bool multiline_comment(const std::string& str) {
  using std::begin;
  using std::end;
  if (str.compare(0, 2, "{~") == 0) {
    int nest = 0;
    for(auto it = begin(str); std::next(it) != end(str); ++it) {
      std::string tk(it, std::next(it, 2));
      if (tk == "{~") {
        ++nest;
      } else if(tk == "~}") {
        --nest;
      }
    }
    bool closed = (nest == 0 && str.compare(str.size()-2, 2, "~}") == 0);
    return (nest > 0 || closed);
  }
  return false;
}

bool comment(const std::string& str) {
  return (singleline_comment(str) || multiline_comment(str));
}

bool string_token(const std::string& str) {
  using std::begin;
  using std::end;
  if (str.front() == '"') {
    bool escaped = false;
    for(auto it = std::next(begin(str)); it != end(str); ++it) {
      if (*it == '\\') {
        escaped = true;
      } else if (*it == '"' && (!escaped)) {
        return std::next(it) == end(str);
      } else {
        escaped = false;
      }
    }
  }
  return false;
}

TokenType match_type(std::string const& str) {
  if (comment(str)) return TokenType::IGNORE;
  if (symbol(str)) return TokenType::SYMBOL;
  if (identifier(str)) return TokenType::IDENTIFIER;
  if (decimal_integer(str)) return TokenType::NUMBER;
  if (string_token(str)) return TokenType::STRING;
  if (ignore(str)) return TokenType::IGNORE;
  return TokenType::UNKNOWN;
}

std::string extract_string(const std::string& str) {
  if (str.front() == '"') {
    bool escaped = false;
    std::string new_str;
    for(auto c : str) {
      if (escaped) {
        if(c == '"') new_str.push_back('"');
        if(c == 'n') new_str.push_back('\n');
        escaped = false;
      } else if (c == '\\') {
        escaped = true;
      } else if (c != '"') {
        new_str.push_back(c);
      }
    }
    return new_str;
  }
  return str;
}

TokenVector tokenize(std::istream& is) {
  std::string str, code;
  while (std::getline(is, str)) {
    code += str + '\n';
  }
  TokenVector tokens;
  str.clear();
  TokenType prev = TokenType::UNKNOWN;
  int line = 1;
  for (char c : code) {
    TokenType next = match_type(str + c);
    if (prev != TokenType::UNKNOWN && next == TokenType::UNKNOWN) {
      if (prev != TokenType::IGNORE) {
        tokens.push_back(Token(prev, extract_string(str), line));
      }
      str = c;
      prev = match_type(str);
    } else {
      str += c;
      prev = next;
    }
    if (c == '\n') ++line;
  }
  return tokens;
}

bool operator==(Token const& lhs, Token const& rhs) {
  return lhs.type() == rhs.type()
      && lhs.str()  == rhs.str()
      && lhs.line() == rhs.line();
}
bool operator!=(Token const& lhs, Token const& rhs) {
  return !( lhs == rhs );
}

}  // namespace klang
<commit_msg>std::string::compare を使うのをやめて、std::equal を使うように<commit_after>#include "lexer.hpp"

#include <cctype>
#include <algorithm>
#include <vector>

namespace klang {

Token::Token()
  : type_(TokenType::UNKNOWN), str_(), line_(-1)
{}

Token::Token(TokenType type, const std::string& str, int line)
  : type_(type), str_(str), line_(line)
{}

TokenType Token::type() const { return type_; }
std::string Token::str() const { return str_; }
int Token::line() const { return line_; }

bool alphabet(char c) {
  return std::isalpha(c);
}

bool alphabet_or_bar(char c) {
  return (c == '_' || alphabet(c));
}

bool nonzero_digit(char c) {
  return (c >= '1' && c <= '9');
}

bool decimal_digit(char c) {
  return std::isdigit(c);
}

bool identifier(const std::string& str) {
  if (str.empty() || !alphabet_or_bar(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!alphabet_or_bar(c) && !decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool decimal_integer(const std::string& str) {
  if (str == "0") return true;
  if (str.empty() || !nonzero_digit(str.front())) {
    return false;
  }
  for (char c : str) {
    if (!decimal_digit(c)) {
      return false;
    }
  }
  return true;
}

bool symbol(const std::string& str) {
  using std::begin;
  using std::end;
  static std::vector<std::string> const symbol_list = {
    "~", "+", "-", "*", "/", "%",
    ":=", ":+=", ":-=", ":*=", ":/=", ":%=",
    "=", "=/", "<", ">", "<=", ">=",
    ";", "(", ")", "{", "}", "->", "~}",
    "and", "or", "not", "int", "def", "var",
    "if", "else", "while", "for", "break", "continue", "return"
  };
  return (std::find(begin(symbol_list), end(symbol_list), str) != end(symbol_list));
}

bool ignore(const std::string& str) {
  if(str.size() != 1) return false;
  return std::isspace(str[0]);
}

bool singleline_comment(const std::string& str) {
  using std::begin;
  using std::end;
  bool inside = (str.back() == '\n' || str.find("\n") == std::string::npos);
  return std::equal(begin(str), std::next(begin(str), 2), "~~") && inside;
}

bool multiline_comment(const std::string& str) {
  using std::begin;
  using std::end;
  if (std::equal(begin(str), std::next(begin(str), 2), "{~")) {
    int nest = 0;
    for(auto it = begin(str); std::next(it) != end(str); ++it) {
      std::string tk(it, std::next(it, 2));
      if (tk == "{~") {
        ++nest;
      } else if(tk == "~}") {
        --nest;
      }
    }
    bool closed = (nest == 0 && std::equal(std::prev(end(str), 2), std::prev(end(str)), "~}"));
    return (nest > 0 || closed);
  }
  return false;
}

bool comment(const std::string& str) {
  return (singleline_comment(str) || multiline_comment(str));
}

bool string_token(const std::string& str) {
  using std::begin;
  using std::end;
  if (str.front() == '"') {
    bool escaped = false;
    for(auto it = std::next(begin(str)); it != end(str); ++it) {
      if (*it == '\\') {
        escaped = true;
      } else if (*it == '"' && (!escaped)) {
        return std::next(it) == end(str);
      } else {
        escaped = false;
      }
    }
  }
  return false;
}

TokenType match_type(std::string const& str) {
  if (comment(str)) return TokenType::IGNORE;
  if (symbol(str)) return TokenType::SYMBOL;
  if (identifier(str)) return TokenType::IDENTIFIER;
  if (decimal_integer(str)) return TokenType::NUMBER;
  if (string_token(str)) return TokenType::STRING;
  if (ignore(str)) return TokenType::IGNORE;
  return TokenType::UNKNOWN;
}

std::string extract_string(const std::string& str) {
  if (str.front() == '"') {
    bool escaped = false;
    std::string new_str;
    for(auto c : str) {
      if (escaped) {
        if(c == '"') new_str.push_back('"');
        if(c == 'n') new_str.push_back('\n');
        escaped = false;
      } else if (c == '\\') {
        escaped = true;
      } else if (c != '"') {
        new_str.push_back(c);
      }
    }
    return new_str;
  }
  return str;
}

TokenVector tokenize(std::istream& is) {
  std::string str, code;
  while (std::getline(is, str)) {
    code += str + '\n';
  }
  TokenVector tokens;
  str.clear();
  TokenType prev = TokenType::UNKNOWN;
  int line = 1;
  for (char c : code) {
    TokenType next = match_type(str + c);
    if (prev != TokenType::UNKNOWN && next == TokenType::UNKNOWN) {
      if (prev != TokenType::IGNORE) {
        tokens.push_back(Token(prev, extract_string(str), line));
      }
      str = c;
      prev = match_type(str);
    } else {
      str += c;
      prev = next;
    }
    if (c == '\n') ++line;
  }
  return tokens;
}

bool operator==(Token const& lhs, Token const& rhs) {
  return lhs.type() == rhs.type()
      && lhs.str()  == rhs.str()
      && lhs.line() == rhs.line();
}
bool operator!=(Token const& lhs, Token const& rhs) {
  return !( lhs == rhs );
}

}  // namespace klang
<|endoftext|>