Introduce integer constants

We can now declare a "const int" variable in an AIDL interface, in
addition to methods. These constants will become static members of the
interface/base class in the generated code.

Change-Id: I6b690ecbbe2acae37abb106510e42283f0753f26
Test: Unit and integration tests updated and pass
Bug: 23600061
Signed-off-by: Casey Dahlin <sadmac@google.com>
diff --git a/aidl.cpp b/aidl.cpp
index 6cfdee3..ffb9e14 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -441,7 +441,7 @@
                          lineno, package);
       types->AddParcelableType(doc, filename);
     } else if (decl == "interface") {
-      auto temp = new std::vector<std::unique_ptr<AidlMethod>>();
+      auto temp = new std::vector<std::unique_ptr<AidlMember>>();
       AidlInterface doc(class_name, lineno, "", false, temp, package);
       types->AddBinderType(doc, filename);
     } else {
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 02eb27d..ed05860 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -87,6 +87,10 @@
   return ret;
 }
 
+AidlConstant::AidlConstant(std::string name, int32_t value)
+    : name_(name),
+      value_(value) {}
+
 AidlMethod::AidlMethod(bool oneway, AidlType* type, std::string name,
                        std::vector<std::unique_ptr<AidlArgument>>* args,
                        unsigned line, const std::string& comments, int id)
@@ -143,15 +147,28 @@
 
 AidlInterface::AidlInterface(const std::string& name, unsigned line,
                              const std::string& comments, bool oneway,
-                             std::vector<std::unique_ptr<AidlMethod>>* methods,
+                             std::vector<std::unique_ptr<AidlMember>>* members,
                              const std::vector<std::string>& package)
     : name_(name),
       comments_(comments),
       line_(line),
       oneway_(oneway),
-      methods_(std::move(*methods)),
       package_(package) {
-  delete methods;
+  for (auto& member : *members) {
+    AidlMember* local = member.release();
+    AidlMethod* method = local->AsMethod();
+    AidlConstant* constant = local->AsConstant();
+
+    if (method) {
+      methods_.emplace_back(method);
+    } else if (constant) {
+      constants_.emplace_back(constant);
+    } else {
+      LOG(FATAL) << "Member is neither method nor constant!";
+    }
+  }
+
+  delete members;
 }
 
 std::string AidlInterface::GetPackage() const {
diff --git a/aidl_language.h b/aidl_language.h
index beaea83..4dcb5ff 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -87,7 +87,38 @@
   DISALLOW_COPY_AND_ASSIGN(AidlArgument);
 };
 
-class AidlMethod {
+class AidlMethod;
+class AidlConstant;
+class AidlMember : public AidlNode {
+ public:
+  AidlMember() = default;
+  virtual ~AidlMember() = default;
+
+  virtual AidlMethod* AsMethod() { return nullptr; }
+  virtual AidlConstant* AsConstant() { return nullptr; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AidlMember);
+};
+
+class AidlConstant : public AidlMember {
+ public:
+  AidlConstant(std::string name, int32_t value);
+  virtual ~AidlConstant() = default;
+
+  const std::string& GetName() const { return name_; }
+  int GetValue() const { return value_; }
+
+  AidlConstant* AsConstant() override { return this; }
+
+ private:
+  std::string name_;
+  int32_t value_;
+
+  DISALLOW_COPY_AND_ASSIGN(AidlConstant);
+};
+
+class AidlMethod : public AidlMember {
  public:
   AidlMethod(bool oneway, AidlType* type, std::string name,
              std::vector<std::unique_ptr<AidlArgument>>* args,
@@ -97,6 +128,8 @@
              unsigned line, const std::string& comments, int id);
   virtual ~AidlMethod() = default;
 
+  AidlMethod* AsMethod() override { return this; }
+
   const std::string& GetComments() const { return comments_; }
   const AidlType& GetType() const { return *type_; }
   bool IsOneway() const { return oneway_; }
@@ -205,7 +238,7 @@
  public:
   AidlInterface(const std::string& name, unsigned line,
                 const std::string& comments, bool oneway_,
-                std::vector<std::unique_ptr<AidlMethod>>* methods,
+                std::vector<std::unique_ptr<AidlMember>>* members,
                 const std::vector<std::string>& package);
   virtual ~AidlInterface() = default;
 
@@ -215,6 +248,8 @@
   bool IsOneway() const { return oneway_; }
   const std::vector<std::unique_ptr<AidlMethod>>& GetMethods() const
       { return methods_; }
+  const std::vector<std::unique_ptr<AidlConstant>>& GetConstants() const
+      { return constants_; }
   std::string GetPackage() const;
   std::string GetCanonicalName() const;
   const std::vector<std::string>& GetSplitPackage() const { return package_; }
@@ -225,6 +260,7 @@
   unsigned line_;
   bool oneway_;
   std::vector<std::unique_ptr<AidlMethod>> methods_;
+  std::vector<std::unique_ptr<AidlConstant>> constants_;
   std::vector<std::string> package_;
 
   DISALLOW_COPY_AND_ASSIGN(AidlInterface);
diff --git a/aidl_language_l.l b/aidl_language_l.l
index 78d4cc2..1bd587b 100644
--- a/aidl_language_l.l
+++ b/aidl_language_l.l
@@ -18,7 +18,7 @@
 
 identifier  [_a-zA-Z][_a-zA-Z0-9]*
 whitespace  ([ \t\r]+)
-idvalue     (0|[1-9][0-9]*)
+intvalue    (0|[1-9][0-9]*)
 
 %%
 %{
@@ -66,10 +66,12 @@
 parcelable            { return yy::parser::token::PARCELABLE; }
 import                { return yy::parser::token::IMPORT; }
 package               { return yy::parser::token::PACKAGE; }
+int                   { return yy::parser::token::INT; }
 in                    { return yy::parser::token::IN; }
 out                   { return yy::parser::token::OUT; }
 inout                 { return yy::parser::token::INOUT; }
 from                  { return yy::parser::token::FROM; }
+const                 { return yy::parser::token::CONST; }
 
 interface             { yylval->token = new AidlToken("interface", extra_text);
                         return yy::parser::token::INTERFACE;
@@ -82,8 +84,8 @@
 {identifier}          { yylval->token = new AidlToken(yytext, extra_text);
                         return yy::parser::token::IDENTIFIER;
                       }
-{idvalue}             { yylval->integer = std::stoi(yytext);
-                        return yy::parser::token::IDVALUE; }
+{intvalue}            { yylval->integer = std::stoi(yytext);
+                        return yy::parser::token::INTVALUE; }
 
     /* syntax error! */
 .                     { printf("UNKNOWN(%s)", yytext);
diff --git a/aidl_language_y.y b/aidl_language_y.y
index 789edab..993f75b 100644
--- a/aidl_language_y.y
+++ b/aidl_language_y.y
@@ -26,7 +26,8 @@
     AidlArgument::Direction direction;
     std::vector<std::unique_ptr<AidlArgument>>* arg_list;
     AidlMethod* method;
-    std::vector<std::unique_ptr<AidlMethod>>* methods;
+    AidlConstant* constant;
+    std::vector<std::unique_ptr<AidlMember>>* members;
     AidlQualifiedName* qname;
     AidlInterface* interface_obj;
     AidlParcelable* parcelable;
@@ -34,16 +35,17 @@
 }
 
 %token<token> IDENTIFIER INTERFACE ONEWAY C_STR
-%token<integer> IDVALUE
+%token<integer> INTVALUE
 
 %token '(' ')' ',' '=' '[' ']' '<' '>' '.' '{' '}' ';'
-%token IN OUT INOUT PACKAGE IMPORT PARCELABLE FROM
+%token IN OUT INOUT PACKAGE IMPORT PARCELABLE FROM CONST INT
 
 %type<parcelable_list> parcelable_decls
 %type<parcelable> parcelable_decl
-%type<methods> methods
+%type<members> members
 %type<interface_obj> interface_decl
 %type<method> method_decl
+%type<constant> constant_decl
 %type<type> type
 %type<arg_list> arg_list
 %type<arg> arg
@@ -68,7 +70,9 @@
  : IDENTIFIER
   { $$ = $1; }
  | FROM
-  { $$ = new AidlToken("from", ""); };
+  { $$ = new AidlToken("from", ""); }
+ | INT
+  { $$ = new AidlToken("int", ""); };
 
 package
  : {}
@@ -126,20 +130,20 @@
   };
 
 interface_decl
- : INTERFACE identifier '{' methods '}' {
+ : INTERFACE identifier '{' members '}' {
     $$ = new AidlInterface($2->GetText(), @2.begin.line, $1->GetComments(),
                            false, $4, ps->Package());
     delete $1;
     delete $2;
   }
- | ONEWAY INTERFACE identifier '{' methods '}' {
+ | ONEWAY INTERFACE identifier '{' members '}' {
     $$ = new AidlInterface($3->GetText(), @3.begin.line, $1->GetComments(),
                            true, $5, ps->Package());
     delete $1;
     delete $2;
     delete $3;
   }
- | INTERFACE error '{' methods '}' {
+ | INTERFACE error '{' members '}' {
     fprintf(stderr, "%s:%d: syntax error in interface declaration.  Expected type name, saw \"%s\"\n",
             ps->FileName().c_str(), @2.begin.line, $2->GetText().c_str());
     $$ = NULL;
@@ -154,18 +158,25 @@
     delete $2;
   };
 
-methods
+members
  :
-  { $$ = new std::vector<std::unique_ptr<AidlMethod>>(); }
- | methods method_decl
-  { $1->push_back(std::unique_ptr<AidlMethod>($2)); }
- | methods error ';' {
+  { $$ = new std::vector<std::unique_ptr<AidlMember>>(); }
+ | members method_decl
+  { $1->push_back(std::unique_ptr<AidlMember>($2)); }
+ | members constant_decl
+  { $1->push_back(std::unique_ptr<AidlMember>($2)); }
+ | members error ';' {
     fprintf(stderr, "%s:%d: syntax error before ';' "
-                    "(expected method declaration)\n",
+                    "(expected method or constant declaration)\n",
             ps->FileName().c_str(), @3.begin.line);
     $$ = $1;
   };
 
+constant_decl
+ : CONST INT identifier '=' INTVALUE ';' {
+    $$ = new AidlConstant($3->GetText(), $5);
+ };
+
 method_decl
  : type identifier '(' arg_list ')' ';' {
     $$ = new AidlMethod(false, $1, $2->GetText(), $4, @2.begin.line,
@@ -178,12 +189,12 @@
     delete $1;
     delete $3;
   }
- | type identifier '(' arg_list ')' '=' IDVALUE ';' {
+ | type identifier '(' arg_list ')' '=' INTVALUE ';' {
     $$ = new AidlMethod(false, $1, $2->GetText(), $4, @2.begin.line,
                         $1->GetComments(), $7);
     delete $2;
   }
- | ONEWAY type identifier '(' arg_list ')' '=' IDVALUE ';' {
+ | ONEWAY type identifier '(' arg_list ')' '=' INTVALUE ';' {
     $$ = new AidlMethod(true, $2, $3->GetText(), $5, @3.begin.line,
                         $1->GetComments(), $8);
     delete $1;
diff --git a/ast_cpp.cpp b/ast_cpp.cpp
index f67addb..b5eb69c 100644
--- a/ast_cpp.cpp
+++ b/ast_cpp.cpp
@@ -72,7 +72,15 @@
   private_members_.push_back(std::move(member));
 }
 
-Enum::EnumField::EnumField(const string& k, const string&v)
+ConstDecl::ConstDecl(const std::string& name, int value)
+    : name_(name),
+      value_(value) {}
+
+void ConstDecl::Write(CodeWriter* to) const {
+  to->Write("static constexpr int32_t %s = %d;\n", name_.c_str(), value_);
+}
+
+Enum::EnumField::EnumField(const string& k, const string& v)
     : key(k),
       value(v) {}
 
diff --git a/ast_cpp.h b/ast_cpp.h
index 1aacf9e..0d7bfc6 100644
--- a/ast_cpp.h
+++ b/ast_cpp.h
@@ -73,6 +73,20 @@
   DISALLOW_COPY_AND_ASSIGN(ClassDecl);
 };  // class ClassDecl
 
+class ConstDecl : public Declaration {
+ public:
+  ConstDecl(const std::string& name, int value);
+  virtual ~ConstDecl() = default;
+
+  void Write(CodeWriter* to) const override;
+
+ private:
+  std::string name_;
+  int value_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConstDecl);
+};  // class ConstDecl
+
 class Enum : public Declaration {
  public:
   explicit Enum(const std::string& name);
diff --git a/ast_java.cpp b/ast_java.cpp
index 6689111..c347d30 100644
--- a/ast_java.cpp
+++ b/ast_java.cpp
@@ -578,6 +578,13 @@
 }
 
 void
+Constant::Write(CodeWriter* to) const
+{
+    WriteModifiers(to, STATIC | FINAL | PUBLIC, ALL_MODIFIERS);
+    to->Write("int %s = %d;\n", name.c_str(), value);
+}
+
+void
 Class::Write(CodeWriter* to) const
 {
     size_t N, i;
diff --git a/ast_java.h b/ast_java.h
index 267faba..f852e88 100644
--- a/ast_java.h
+++ b/ast_java.h
@@ -356,6 +356,17 @@
     void Write(CodeWriter* to) const override;
 };
 
+struct Constant : public ClassElement
+{
+    string name;
+    int value;
+
+    Constant() = default;
+    virtual ~Constant() = default;
+
+    void Write(CodeWriter* to) const override;
+};
+
 struct Class : public ClassElement
 {
     enum {
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 9307c28..001ea44 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -697,6 +697,12 @@
       "DECLARE_META_INTERFACE",
       ArgList{vector<string>{ClassName(interface, ClassNames::BASE)}}}});
 
+  for (const auto& constant : interface.GetConstants()) {
+    unique_ptr<ConstDecl> declaration{
+        new ConstDecl(constant->GetName(), constant->GetValue())};
+    if_class->AddPublic(std::move(declaration));
+  }
+
   unique_ptr<Enum> call_enum{new Enum{"Call"}};
   for (const auto& method : interface.GetMethods()) {
     // Each method gets an enum entry and pure virtual declaration.
diff --git a/generate_cpp_unittest.cpp b/generate_cpp_unittest.cpp
index 8949e22..0138150 100644
--- a/generate_cpp_unittest.cpp
+++ b/generate_cpp_unittest.cpp
@@ -43,6 +43,7 @@
 R"(package android.os;
 import foo.IFooType;
 interface IComplexTypeInterface {
+  const int MY_CONSTANT = 3;
   int[] Send(in int[] goes_in, inout double[] goes_in_and_out, out boolean[] goes_out);
   oneway void Piff(int times);
   IFooType TakesABinder(IFooType f);
@@ -606,6 +607,7 @@
 class IComplexTypeInterface : public ::android::IInterface {
 public:
 DECLARE_META_INTERFACE(ComplexTypeInterface);
+static constexpr int32_t MY_CONSTANT = 3;
 virtual ::android::binder::Status Send(const ::std::vector<int32_t>& goes_in, ::std::vector<double>* goes_in_and_out, ::std::vector<bool>* goes_out, ::std::vector<int32_t>* _aidl_return) = 0;
 virtual ::android::binder::Status Piff(int32_t times) = 0;
 virtual ::android::binder::Status TakesABinder(const ::android::sp<::foo::IFooType>& f, ::android::sp<::foo::IFooType>* _aidl_return) = 0;
diff --git a/generate_java_binder.cpp b/generate_java_binder.cpp
index b28d843..daff480 100644
--- a/generate_java_binder.cpp
+++ b/generate_java_binder.cpp
@@ -262,6 +262,16 @@
 
 
 static void
+generate_constant(const AidlConstant& constant, Class* interface)
+{
+    Constant* decl = new Constant;
+        decl->name = constant.GetName();
+        decl->value = constant.GetValue();
+
+    interface->elements.push_back(decl);
+}
+
+static void
 generate_method(const AidlMethod& method, Class* interface,
                 StubClass* stubClass, ProxyClass* proxyClass, int index,
                 JavaTypeNamespace* types)
@@ -543,12 +553,15 @@
     // stub and proxy support for getInterfaceDescriptor()
     generate_interface_descriptors(stub, proxy, types);
 
+    // all the declared constants of the interface
+    for (const auto& item : iface->GetConstants()) {
+        generate_constant(*item, interface);
+    }
+
     // all the declared methods of the interface
-    int index = 0;
     for (const auto& item : iface->GetMethods()) {
         generate_method(*item, interface, stub, proxy,
                         item->GetId(), types);
-        index++;
     }
 
     return interface;
diff --git a/tests/aidl_test_client.cpp b/tests/aidl_test_client.cpp
index 976dc37..5ef3437 100644
--- a/tests/aidl_test_client.cpp
+++ b/tests/aidl_test_client.cpp
@@ -91,7 +91,8 @@
       !RepeatPrimitive(s, &ITestService::RepeatInt, int32_t{1 << 30}) ||
       !RepeatPrimitive(s, &ITestService::RepeatLong, int64_t{1ll << 60}) ||
       !RepeatPrimitive(s, &ITestService::RepeatFloat, float{1.0f/3.0f}) ||
-      !RepeatPrimitive(s, &ITestService::RepeatDouble, double{1.0/3.0})) {
+      !RepeatPrimitive(s, &ITestService::RepeatDouble, double{1.0/3.0}) ||
+      !RepeatPrimitive(s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT)) {
     return false;
   }
 
diff --git a/tests/android/aidl/tests/ITestService.aidl b/tests/android/aidl/tests/ITestService.aidl
index 3d750d7..db80a87 100644
--- a/tests/android/aidl/tests/ITestService.aidl
+++ b/tests/android/aidl/tests/ITestService.aidl
@@ -20,6 +20,9 @@
 import android.aidl.tests.SimpleParcelable;
 
 interface ITestService {
+  // Test that constants are accessible
+  const int TEST_CONSTANT = 42;
+
   // Test that primitives work as parameters and return types.
   boolean RepeatBoolean(boolean token);
   byte RepeatByte(byte token);
diff --git a/tests/java_app/src/android/aidl/tests/TestServiceClient.java b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
index e5b1947..42a60a9 100644
--- a/tests/java_app/src/android/aidl/tests/TestServiceClient.java
+++ b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
@@ -144,6 +144,14 @@
                 }
             }
             {
+                int query = ITestService.TEST_CONSTANT;
+                int response = service.RepeatInt(query);
+                if (query != response) {
+                    mLog.logAndThrow("Repeat with " + query +
+                                     " responded " + response);
+                }
+            }
+            {
                 long query = 1 << 60;
                 long response = service.RepeatLong(query);
                 if (query != response) {
diff --git a/tests/test_data_example_interface.cpp b/tests/test_data_example_interface.cpp
index 3fb299c..eea5e8e 100644
--- a/tests/test_data_example_interface.cpp
+++ b/tests/test_data_example_interface.cpp
@@ -45,6 +45,7 @@
 import android.test.IAuxInterface2;
 
 interface IExampleInterface {
+    const int EXAMPLE_CONSTANT = 3;
     boolean isEnabled();
     int getState();
     String getAddress();
@@ -431,6 +432,7 @@
 static final int TRANSACTION_takesAnInterface = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
 static final int TRANSACTION_takesAParcelable = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
 }
+public static final int EXAMPLE_CONSTANT = 3;
 public boolean isEnabled() throws android.os.RemoteException;
 public int getState() throws android.os.RemoteException;
 public java.lang.String getAddress() throws android.os.RemoteException;