Skip to content
This repository has been archived by the owner on Mar 26, 2020. It is now read-only.

Property declaration support in interfaces #360

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/source/CppGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,20 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
i.consts.map(c => {
refs.find(c.ty, true)
})
i.properties.map(p => {
refs.find(p.ty, true)
})

val self = marshal.typename(ident, i)
val methodNamesInScope = i.methods.map(m => idCpp.method(m.ident))
var localMethods = i.methods
for (p <- i.properties) {
var argSeq = Option(p.ty)
localMethods = localMethods :+ Interface.Method(Ident(s"get_${p.ident.name}", ident.file, ident.loc), Seq.empty, argSeq, Doc(Seq(s"getter for ${p.ident.name}")), false, true)
if (!p.readOnly) {
localMethods = localMethods :+ Interface.Method(Ident(s"set_${p.ident.name}", ident.file, ident.loc), Seq(Field(Ident(s"new_${p.ident.name}", p.ident.file, p.ident.loc), p.ty, Doc(Seq(p.ident)))), None, Doc(Seq(s"setter for ${p.ident.name}")), false, false)
}
}
val methodNamesInScope = localMethods.map(m => idCpp.method(m.ident))

writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => {
writeDoc(w, doc)
Expand All @@ -307,7 +318,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
// Constants
generateHppConstants(w, i.consts)
// Methods
for (m <- i.methods) {
for (m <- localMethods) {
w.wl
writeMethodDoc(w, m, idCpp.local)
val ret = marshal.returnType(m.ret, methodNamesInScope)
Expand Down
24 changes: 24 additions & 0 deletions src/source/JNIGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
i.consts.foreach(c => {
refs.find(c.ty)
})
i.properties.foreach(p => {
refs.find(p.ty)
})

val jniSelf = jniMarshal.helperClass(ident)
val cppSelf = cppMarshal.fqTypename(ident, i) + cppTypeArgs(typeParams)
Expand Down Expand Up @@ -373,6 +376,27 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
m.ret.fold()(r => w.wl(s"return ::djinni::release(${jniMarshal.fromCpp(r, "r")});"))
})
}
for(p <- i.properties) {
nativeHook(s"native_get${idJava.method(p.ident).capitalize}", false, Nil, Option(p.ty), {
w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);")
val methodName = "get_" + idCpp.method(p.ident)
val ret = "auto r = "
val call = s"ref->$methodName"
w.wl(s"${ret}${call}();")
w.wl(s"return ::djinni::release(${jniMarshal.fromCpp(p.ty, "r")});")
})
if(!p.readOnly) {
val setterParam = Iterable(Field(p.ident, p.ty, Doc(Nil)));
nativeHook(s"native_set${idJava.method(p.ident).capitalize}", false, setterParam, Option(null), {
w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);")
val methodName = "set_" + idCpp.method(p.ident)
val call = s"ref->$methodName"
val params = jniMarshal.toCpp(p.ty, "j_" + idJava.local(p.ident))
w.wl(s"${call}(${params});")
w.wl(";")
})
}
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions src/source/JavaGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
i.consts.map(c => {
refs.find(c.ty)
})
i.properties.map(p => {
refs.find(p.ty)
})

if (i.ext.cpp) {
refs.java.add("java.util.concurrent.atomic.AtomicBoolean")
}
Expand All @@ -145,7 +149,16 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
generateJavaConstants(w, i.consts)

val throwException = spec.javaCppException.fold("")(" throws " + _)
for (m <- i.methods if !m.static) {
var localMethods = i.methods
for (p <- i.properties) {
var argSeq = Option(p.ty)
localMethods = localMethods :+ Interface.Method(Ident(s"get_${p.ident.name}", ident.file, ident.loc), Seq.empty, argSeq, Doc(Seq(s"getter for ${p.ident.name}")), false, false)
if (!p.readOnly) {
localMethods = localMethods :+ Interface.Method(Ident(s"set_${p.ident.name}", ident.file, ident.loc), Seq(Field(Ident(s"new_${p.ident.name}", p.ident.file, p.ident.loc), p.ty, Doc(Seq(p.ident)))), None, Doc(Seq(s"setter for ${p.ident.name}")), false, false)
}
}

for (m <- localMethods if !m.static) {
skipFirst { w.wl }
writeMethodDoc(w, m, idJava.local)
val ret = marshal.returnType(m.ret)
Expand All @@ -156,7 +169,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
marshal.nullityAnnotation(m.ret).foreach(w.wl)
w.wl("public abstract " + ret + " " + idJava.method(m.ident) + params.mkString("(", ", ", ")") + throwException + ";")
}
for (m <- i.methods if m.static) {
for (m <- localMethods if m.static) {
skipFirst { w.wl }
writeMethodDoc(w, m, idJava.local)
val ret = marshal.returnType(m.ret)
Expand All @@ -167,6 +180,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
marshal.nullityAnnotation(m.ret).foreach(w.wl)
w.wl("public static native "+ ret + " " + idJava.method(m.ident) + params.mkString("(", ", ", ")") + ";")
}

if (i.ext.cpp) {
w.wl
javaAnnotationHeader.foreach(w.wl)
Expand All @@ -188,7 +202,8 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
w.wl("destroy();")
w.wl("super.finalize();")
}
for (m <- i.methods if !m.static) { // Static methods not in CppProxy

for (m <- localMethods if !m.static) { // Static methods not in CppProxy
val ret = marshal.returnType(m.ret)
val returnStmt = m.ret.fold("")(_ => "return ")
val params = m.params.map(p => marshal.paramType(p.ty) + " " + idJava.local(p.ident)).mkString(", ")
Expand Down
7 changes: 7 additions & 0 deletions src/source/ObjcGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
writeObjcFuncDecl(m, w)
w.wl(";")
}
for(p <- i.properties) {
w.wl
writeDoc(w, p.doc)
val nullability = marshal.nullability(p.ty.resolved).fold("")(", " + _)
val readonly = if(p.readOnly) ", readonly" else ""
w.wl(s"@property (nonatomic${nullability}${readonly}) ${marshal.fqFieldType(p.ty)} ${idObjc.field(p.ident)};")
}
for (c <- i.consts if !marshal.canBeConstVariable(c)) {
w.wl
writeDoc(w, c.doc)
Expand Down
20 changes: 20 additions & 0 deletions src/source/ObjcppGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
i.consts.map(c => {
refs.find(c.ty)
})
i.properties.map(p => {
refs.find(p.ty)
})

val self = objcMarshal.typename(ident, i)
val cppSelf = cppMarshal.fqTypename(ident, i)
Expand Down Expand Up @@ -197,6 +200,23 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
}
}

for (p <- i.properties) {
w.wl
w.wl(s"- (${marshal.fqFieldType(p.ty)})${idObjc.method(p.ident)}")
w.braced {
val call = "_cppRefHandle.get()->get_" + idCpp.method(p.ident) + "();"
w.wl(s"auto objcpp_result_ = ${call}")
w.wl(s"return ${objcppMarshal.fromCpp(p.ty, "objcpp_result_")};")
}
if(!p.readOnly) {
w.wl(s"- (void)set${idObjc.method(p.ident).capitalize}:(${marshal.fqFieldType(p.ty)})${idCpp.method(p.ident)}")
w.braced {
val call = s"_cppRefHandle.get()->set_${idCpp.method(p.ident)}(${objcppMarshal.toCpp(p.ty, idCpp.method(p.ident))})";
w.wl(s"${call};")
}
}
}

if (i.consts.nonEmpty) {
w.wl
generateObjcConstants(w, i.consts, self, ObjcConstantType.ConstMethod)
Expand Down
3 changes: 2 additions & 1 deletion src/source/ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ object Record {
}
}

case class Interface(ext: Ext, methods: Seq[Interface.Method], consts: Seq[Const]) extends TypeDef
case class Interface(ext: Ext, methods: Seq[Interface.Method], consts: Seq[Const], properties: Seq[Interface.Property]) extends TypeDef
object Interface {
case class Method(ident: Ident, params: Seq[Field], ret: Option[TypeRef], doc: Doc, static: Boolean, const: Boolean)
case class Property(ident: Ident, ty: TypeRef, doc: Doc, readOnly: Boolean)
}

case class Field(ident: Ident, ty: TypeRef, doc: Doc)
19 changes: 15 additions & 4 deletions src/source/parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ package djinni

import java.io.{File, FileNotFoundException, InputStreamReader, FileInputStream, Writer}

import djinni.ast.Interface.Method
import djinni.ast.Interface.{Method, Property}
import djinni.ast.Record.DerivingType.DerivingType
import djinni.syntax._
import djinni.ast._
import java.util.{Map => JMap}

import org.yaml.snakeyaml.Yaml

import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.util.control.Breaks._
Expand Down Expand Up @@ -150,19 +152,20 @@ private object IdlParser extends RegexParsers {
}

def interfaceHeader = "interface" ~> extInterface
def interface: Parser[Interface] = interfaceHeader ~ bracesList(method | const) ^^ {
def interface: Parser[Interface] = interfaceHeader ~ bracesList(method | const | property) ^^ {
case ext~items => {
val methods = items collect {case m: Method => m}
val consts = items collect {case c: Const => c}
Interface(ext, methods, consts)
val properties = items collect {case p: Property => p}
Interface(ext, methods, consts, properties)
}
}

def externTypeDecl: Parser[TypeDef] = externEnum | externFlags | externInterface | externRecord
def externEnum: Parser[Enum] = enumHeader ^^ { case _ => Enum(List(), false) }
def externFlags: Parser[Enum] = flagsHeader ^^ { case _ => Enum(List(), true) }
def externRecord: Parser[Record] = recordHeader ~ opt(deriving) ^^ { case ext~deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) }
def externInterface: Parser[Interface] = interfaceHeader ^^ { case ext => Interface(ext, List(), List()) }
def externInterface: Parser[Interface] = interfaceHeader ^^ { case ext => Interface(ext, List(), List(), List()) }

def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ {
case "static " => true
Expand All @@ -172,6 +175,10 @@ private object IdlParser extends RegexParsers {
case "const " => true
case "" => false
}
def readOnlyLabel: Parser[Boolean] = ("readonly ".r | "".r) ^^ {
case "readonly " => true
case "" => false
}
def method: Parser[Interface.Method] = doc ~ staticLabel ~ constLabel ~ ident ~ parens(repsepend(field, ",")) ~ opt(ret) ^^ {
case doc~staticLabel~constLabel~ ident~params~ret => Interface.Method(ident, params, ret, doc, staticLabel, constLabel)
}
Expand All @@ -196,6 +203,10 @@ private object IdlParser extends RegexParsers {
case doc~_~ident~_~typeRef~_~value => Const(ident, typeRef, value, doc)
}

def property: Parser[Interface.Property] = doc ~ readOnlyLabel ~ ident ~ ret ^^ {
case doc~readOnlyLabel~ident~ret => Interface.Property(ident, ret, doc, readOnlyLabel)
}

def typeRef: Parser[TypeRef] = typeExpr ^^ TypeRef
def typeExpr: Parser[TypeExpr] = ident ~ typeList(typeExpr) ^^ {
case ident~typeArgs => TypeExpr(ident, typeArgs)
Expand Down
5 changes: 5 additions & 0 deletions src/source/resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ private def resolveInterface(scope: Scope, i: Interface) {
case _ =>
}
}

for (p <- i.properties) {
dupeChecker.check(p.ident)
resolveRef(scope, p.ty)
}
// Name checking for constants. Type check only possible after resolving record field types.
for (c <- i.consts) {
dupeChecker.check(c.ident)
Expand Down
2 changes: 2 additions & 0 deletions test-suite/djinni/common.djinni
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import "properties.djinni"
@import "set.djinni"
@import "derivings.djinni"
@import "nested_collection.djinni"
Expand All @@ -15,3 +16,4 @@
@import "extended_record.djinni"
@import "varnames.djinni"
@import "relative_paths.djinni"

11 changes: 11 additions & 0 deletions test-suite/djinni/properties.djinni
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
properties_test_helper = interface +c {

item: i32;
test_string: string;
other_method(argument: string) : string;
test_list: list<i32>;
readonly read_only_bool: bool;

static create_new(): properties_test_helper;
}

43 changes: 43 additions & 0 deletions test-suite/generated-src/cpp/properties_test_helper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from properties.djinni

#pragma once

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace testsuite {

class PropertiesTestHelper {
public:
virtual ~PropertiesTestHelper() {}

virtual std::string other_method(const std::string & argument) = 0;

static std::shared_ptr<PropertiesTestHelper> create_new();

/**getter for item */
virtual int32_t get_item() const = 0;

/**setter for item */
virtual void set_item(int32_t new_item) = 0;

/**getter for test_string */
virtual std::string get_test_string() const = 0;

/**setter for test_string */
virtual void set_test_string(const std::string & new_test_string) = 0;

/**getter for test_list */
virtual std::vector<int32_t> get_test_list() const = 0;

/**setter for test_list */
virtual void set_test_list(const std::vector<int32_t> & new_test_list) = 0;

/**getter for read_only_bool */
virtual bool get_read_only_bool() const = 0;
};

} // namespace testsuite
1 change: 1 addition & 0 deletions test-suite/generated-src/inFileList.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
djinni/all.djinni
djinni/common.djinni
djinni/properties.djinni
djinni/set.djinni
djinni/derivings.djinni
djinni/nested_collection.djinni
Expand Down
Loading