Commit e9c23acc authored by Xianwen Chen's avatar Xianwen Chen Committed by Andrew Twyman

Safe ObjC constants [breaking change]

Fixes #171.  Djinni constants which are not primitives, enums, or strings must
now be accessed in Objective-C class members, rather than constant
variables.  This avoids unsafe ordering of initialization of constant objects.
parent 54df6f11
/**
* Copyright 2016 Dropbox, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package djinni
import djinni.ast.Record.DerivingType
import djinni.ast._
import djinni.generatorTools._
import djinni.meta._
import djinni.syntax.Error
import djinni.writer.IndentWriter
import scala.collection.mutable
import scala.collection.parallel.immutable
abstract class BaseObjcGenerator(spec: Spec) extends Generator(spec) {
val marshal = new ObjcMarshal(spec)
object ObjcConstantType extends Enumeration {
val ConstVariable, ConstMethod = Value
}
def writeObjcConstVariableDecl(w: IndentWriter, c: Const, s: String): Unit = {
val nullability = marshal.nullability(c.ty.resolved).fold("")(" __" + _)
val td = marshal.fqFieldType(c.ty) + nullability
// MBinary | MList | MSet | MMap are not allowed for constants.
w.w(s"${td} const $s${idObjc.const(c.ident)}")
}
/**
* Gererate the definition of Objc constants.
*/
def generateObjcConstants(w: IndentWriter, consts: Seq[Const], selfName: String,
genType: ObjcConstantType.Value) = {
def boxedPrimitive(ty: TypeRef): String = {
val (_, needRef) = marshal.toObjcType(ty)
if (needRef) "@" else ""
}
def writeObjcConstValue(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match {
case l: Long => w.w(boxedPrimitive(ty) + l.toString)
case d: Double if marshal.fieldType(ty) == "float" => w.w(boxedPrimitive(ty) + d.toString + "f")
case d: Double => w.w(boxedPrimitive(ty) + d.toString)
case b: Boolean => w.w(boxedPrimitive(ty) + (if (b) "YES" else "NO"))
case s: String => w.w("@" + s)
case e: EnumValue => w.w(idObjc.enum(e.ty + "_" + e.name))
case v: ConstRef => w.w(selfName + idObjc.const (v.name))
case z: Map[_, _] => { // Value is record
val recordMdef = ty.resolved.base.asInstanceOf[MDef]
val record = recordMdef.body.asInstanceOf[Record]
val vMap = z.asInstanceOf[Map[String, Any]]
val head = record.fields.head
w.w(s"[[${marshal.typename(ty)} alloc] initWith${IdentStyle.camelUpper(head.ident)}:")
writeObjcConstValue(w, head.ty, vMap.apply(head.ident))
w.nestedN(2) {
val skipFirst = SkipFirst()
for (f <- record.fields) skipFirst {
w.wl
w.w(s"${idObjc.field(f.ident)}:")
writeObjcConstValue(w, f.ty, vMap.apply(f.ident))
}
}
w.w("]")
}
}
def writeObjcConstMethImpl(c: Const, w: IndentWriter) {
val label = "+"
val nullability = marshal.nullability(c.ty.resolved).fold("")(" __" + _)
val ret = marshal.fqFieldType(c.ty) + nullability
val decl = s"$label ($ret)${idObjc.method(c.ident)}"
writeAlignedObjcCall(w, decl, List(), "", p => ("",""))
w.wl
w.braced {
var static_var = s"s_${idObjc.method(c.ident)}"
w.w(s"static ${marshal.fqFieldType(c.ty)} const ${static_var} = ")
writeObjcConstValue(w, c.ty, c.value)
w.wl(";")
w.wl(s"return $static_var;")
}
}
genType match {
case ObjcConstantType.ConstVariable => {
for (c <- consts if marshal.canBeConstVariable(c)) {
w.wl
writeObjcConstVariableDecl(w, c, selfName)
w.w(s" = ")
writeObjcConstValue(w, c.ty, c.value)
w.wl(";")
}
}
case ObjcConstantType.ConstMethod => {
for (c <- consts if !marshal.canBeConstVariable(c)) {
writeObjcConstMethImpl(c, w)
w.wl
}
}
}
}
}
......@@ -26,9 +26,7 @@ import djinni.writer.IndentWriter
import scala.collection.mutable
import scala.collection.parallel.immutable
class ObjcGenerator(spec: Spec) extends Generator(spec) {
val marshal = new ObjcMarshal(spec)
class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
class ObjcRefs() {
var body = mutable.TreeSet[String]()
......@@ -65,58 +63,17 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
def bodyName(ident: String): String = idObjc.ty(ident) + "." + spec.objcppExt // Must be a Obj-C++ file in case the constants are not compile-time constant expressions
def writeObjcConstVariable(w: IndentWriter, c: Const, s: String): Unit = {
def writeObjcConstMethDecl(c: Const, w: IndentWriter) {
val label = "+"
val nullability = marshal.nullability(c.ty.resolved).fold("")(" __" + _)
val td = marshal.fqFieldType(c.ty) + nullability
// MBinary | MList | MSet | MMap are not allowed for constants.
w.w(s"${td} const $s${idObjc.const(c.ident)}")
}
def generateObjcConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = {
def boxedPrimitive(ty: TypeRef): String = {
val (_, needRef) = toObjcType(ty)
if (needRef) "@" else ""
}
def writeObjcConstValue(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match {
case l: Long => w.w(boxedPrimitive(ty) + l.toString)
case d: Double if marshal.fieldType(ty) == "float" => w.w(boxedPrimitive(ty) + d.toString + "f")
case d: Double => w.w(boxedPrimitive(ty) + d.toString)
case b: Boolean => w.w(boxedPrimitive(ty) + (if (b) "YES" else "NO"))
case s: String => w.w("@" + s)
case e: EnumValue => w.w(idObjc.enum(e.ty + "_" + e.name))
case v: ConstRef => w.w(selfName + idObjc.const (v.name))
case z: Map[_, _] => { // Value is record
val recordMdef = ty.resolved.base.asInstanceOf[MDef]
val record = recordMdef.body.asInstanceOf[Record]
val vMap = z.asInstanceOf[Map[String, Any]]
val head = record.fields.head
w.w(s"[[${marshal.typename(ty)} alloc] initWith${IdentStyle.camelUpper(head.ident)}:")
writeObjcConstValue(w, head.ty, vMap.apply(head.ident))
w.nestedN(2) {
val skipFirst = SkipFirst()
for (f <- record.fields) skipFirst {
w.wl
w.w(s"${idObjc.field(f.ident)}:")
writeObjcConstValue(w, f.ty, vMap.apply(f.ident))
}
}
w.w("]")
}
}
w.wl("#pragma clang diagnostic push")
w.wl("#pragma clang diagnostic ignored " + q("-Wunused-variable"))
for (c <- consts) {
w.wl
writeObjcConstVariable(w, c, selfName)
w.w(s" = ")
writeObjcConstValue(w, c.ty, c.value)
w.wl(";")
}
w.wl
w.wl("#pragma clang diagnostic pop")
val ret = marshal.fqFieldType(c.ty) + nullability
val decl = s"$label ($ret)${idObjc.method(c.ident)}"
writeAlignedObjcCall(w, decl, List(), ";", p => ("",""))
}
/**
* Generate Interface
*/
override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {
val refs = new ObjcRefs()
i.methods.map(m => {
......@@ -138,11 +95,12 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
writeAlignedObjcCall(w, decl, method.params, "", p => (idObjc.field(p.ident), s"(${marshal.paramType(p.ty)})${idObjc.local(p.ident)}"))
}
// Generate the header file for Interface
writeObjcFile(marshal.headerName(ident), origin, refs.header, w => {
for (c <- i.consts) {
for (c <- i.consts if marshal.canBeConstVariable(c)) {
writeDoc(w, c.doc)
w.w(s"extern ")
writeObjcConstVariable(w, c, self)
writeObjcConstVariableDecl(w, c, self)
w.wl(s";")
}
w.wl
......@@ -154,15 +112,23 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
writeObjcFuncDecl(m, w)
w.wl(";")
}
for (c <- i.consts if !marshal.canBeConstVariable(c)) {
w.wl
writeDoc(w, c.doc)
writeObjcConstMethDecl(c, w)
}
w.wl
w.wl("@end")
})
// Generate the implementation file for Interface
if (i.consts.nonEmpty) {
refs.body.add("#import " + q(spec.objcIncludePrefix + marshal.headerName(ident)))
writeObjcFile(bodyName(ident.name), origin, refs.body, w => {
generateObjcConstants(w, i.consts, self)
generateObjcConstants(w, i.consts, self, ObjcConstantType.ConstVariable)
})
// For constants implemented via Methods, we generate their definitions in the
// corresponding ObjcCpp file (i.e.: `ClassName`+Private.mm)
}
}
......@@ -196,6 +162,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
val firstInitializerArg = if(r.fields.isEmpty) "" else IdentStyle.camelUpper("with_" + r.fields.head.ident.name)
// Generate the header file for record
writeObjcFile(marshal.headerName(objcName), origin, refs.header, w => {
writeDoc(w, doc)
w.wl(s"@interface $self : NSObject")
......@@ -209,6 +176,12 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
writeInitializer("-", "init")
if (!r.ext.objc) writeInitializer("+", IdentStyle.camelLower(objcName))
for (c <- r.consts if !marshal.canBeConstVariable(c)) {
w.wl
writeDoc(w, c.doc)
writeObjcConstMethDecl(c, w)
}
for (f <- r.fields) {
w.wl
writeDoc(w, f.doc)
......@@ -224,17 +197,19 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
// Constants come last in case one of them is of the record's type
if (r.consts.nonEmpty) {
w.wl
for (c <- r.consts) {
for (c <- r.consts if marshal.canBeConstVariable(c)) {
writeDoc(w, c.doc)
w.w(s"extern ")
writeObjcConstVariable(w, c, noBaseSelf);
writeObjcConstVariableDecl(w, c, noBaseSelf);
w.wl(s";")
}
}
})
// Generate the implementation file for record
writeObjcFile(bodyName(objcName), origin, refs.body, w => {
if (r.consts.nonEmpty) generateObjcConstants(w, r.consts, noBaseSelf)
if (r.consts.nonEmpty) generateObjcConstants(w, r.consts, noBaseSelf, ObjcConstantType.ConstVariable)
w.wl
w.wl(s"@implementation $self")
w.wl
......@@ -268,6 +243,8 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
w.wl
}
if (r.consts.nonEmpty) generateObjcConstants(w, r.consts, noBaseSelf, ObjcConstantType.ConstMethod)
if (r.derivingTypes.contains(DerivingType.Eq)) {
w.wl("- (BOOL)isEqual:(id)other")
w.braced {
......@@ -439,55 +416,4 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
f(w)
})
}
// TODO: this should be in ObjcMarshal
// Return value: (Type_Name, Is_Class_Or_Not)
def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false)
def toObjcType(ty: TypeRef, needRef: Boolean): (String, Boolean) = toObjcType(ty.resolved, needRef)
def toObjcType(tm: MExpr): (String, Boolean) = toObjcType(tm, false)
def toObjcType(tm: MExpr, needRef: Boolean): (String, Boolean) = {
def f(tm: MExpr, needRef: Boolean): (String, Boolean) = {
tm.base match {
case MOptional =>
// We use "nil" for the empty optional.
assert(tm.args.size == 1)
val arg = tm.args.head
arg.base match {
case MOptional => throw new AssertionError("nested optional?")
case m => f(arg, true)
}
case o =>
val base = o match {
case p: MPrimitive => if (needRef) (p.objcBoxed, true) else (p.objcName, false)
case MString => ("NSString", true)
case MDate => ("NSDate", true)
case MBinary => ("NSData", true)
case MOptional => throw new AssertionError("optional should have been special cased")
case MList => ("NSArray", true)
case MSet => ("NSSet", true)
case MMap => ("NSDictionary", true)
case d: MDef => d.defType match {
case DEnum => if (needRef) ("NSNumber", true) else (idObjc.ty(d.name), false)
case DRecord => (idObjc.ty(d.name), true)
case DInterface =>
val ext = d.body.asInstanceOf[Interface].ext
if (ext.cpp) (s"${idObjc.ty(d.name)}*", false) else (s"id<${idObjc.ty(d.name)}>", false)
}
case e: MExtern => if(needRef) (e.objc.boxed, true) else (e.objc.typename, e.objc.pointer)
case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level")
}
base
}
}
f(tm, needRef)
}
// TODO: this should be in ObjcMarshal
def toObjcTypeDef(ty: TypeRef): String = toObjcTypeDef(ty.resolved, false)
def toObjcTypeDef(ty: TypeRef, needRef: Boolean): String = toObjcTypeDef(ty.resolved, needRef)
def toObjcTypeDef(tm: MExpr): String = toObjcTypeDef(tm, false)
def toObjcTypeDef(tm: MExpr, needRef: Boolean): String = {
val (name, asterisk) = toObjcType(tm, needRef)
name + (if (asterisk) " *" else " ")
}
}
......@@ -146,4 +146,22 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
name + (if(needRef) " *" else "")
}
/**
* This method returns whether we can use global variable to represent a given constant.
*
* We can use global variables for constants which are safe to create during static init, which are numbers
* strings, and optional strings. Anything else needs to be a class method.
*/
def canBeConstVariable(c:Const): Boolean = c.ty.resolved.base match {
case MPrimitive(_,_,_,_,_,_,_,_) => true
case MString => true
case MOptional =>
assert(c.ty.resolved.args.size == 1)
val arg = c.ty.resolved.args.head
arg.base match {
case MString => true
case _ => false
}
case _ => false
}
}
......@@ -25,7 +25,7 @@ import djinni.writer.IndentWriter
import scala.collection.mutable
class ObjcppGenerator(spec: Spec) extends Generator(spec) {
class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
val objcMarshal = new ObjcMarshal(spec)
val objcppMarshal = new ObjcppMarshal(spec)
......@@ -186,6 +186,11 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
}
}
}
if (i.consts.nonEmpty) {
w.wl
generateObjcConstants(w, i.consts, self, ObjcConstantType.ConstMethod)
}
}
if (i.ext.objc) {
......
......@@ -9,6 +9,20 @@
- (nonnull instancetype)init;
+ (nonnull instancetype)constants;
+ (NSNumber * __nullable)optBoolConstant;
+ (NSNumber * __nullable)optI8Constant;
/** opt_i16_constant has documentation. */
+ (NSNumber * __nullable)optI16Constant;
+ (NSNumber * __nullable)optI32Constant;
+ (NSNumber * __nullable)optI64Constant;
/**
* opt_f32_constant has long documentation.
* (Second line of multi-line documentation.
* Indented third line of multi-line documentation.)
*/
+ (NSNumber * __nullable)optF32Constant;
+ (NSNumber * __nullable)optF64Constant;
+ (DBConstantRecord * __nonnull)objectConstant;
@end
/** bool_constant has documentation. */
......@@ -24,22 +38,8 @@ extern float const DBConstantsF32Constant;
* Indented third line of multi-line documentation.)
*/
extern double const DBConstantsF64Constant;
extern NSNumber * __nullable const DBConstantsOptBoolConstant;
extern NSNumber * __nullable const DBConstantsOptI8Constant;
/** opt_i16_constant has documentation. */
extern NSNumber * __nullable const DBConstantsOptI16Constant;
extern NSNumber * __nullable const DBConstantsOptI32Constant;
extern NSNumber * __nullable const DBConstantsOptI64Constant;
/**
* opt_f32_constant has long documentation.
* (Second line of multi-line documentation.
* Indented third line of multi-line documentation.)
*/
extern NSNumber * __nullable const DBConstantsOptF32Constant;
extern NSNumber * __nullable const DBConstantsOptF64Constant;
extern NSString * __nonnull const DBConstantsStringConstant;
extern NSString * __nullable const DBConstantsOptStringConstant;
extern DBConstantRecord * __nonnull const DBConstantsObjectConstant;
/**
* No support for null optional constants
* No support for optional constant records
......
......@@ -3,8 +3,6 @@
#import "DBConstants.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
BOOL const DBConstantsBoolConstant = YES;
......@@ -20,31 +18,12 @@ float const DBConstantsF32Constant = 5.0f;
double const DBConstantsF64Constant = 5.0;
NSNumber * __nullable const DBConstantsOptBoolConstant = @YES;
NSNumber * __nullable const DBConstantsOptI8Constant = @1;
NSNumber * __nullable const DBConstantsOptI16Constant = @2;
NSNumber * __nullable const DBConstantsOptI32Constant = @3;
NSNumber * __nullable const DBConstantsOptI64Constant = @4;
NSNumber * __nullable const DBConstantsOptF32Constant = @5.0;
NSNumber * __nullable const DBConstantsOptF64Constant = @5.0;
NSString * __nonnull const DBConstantsStringConstant = @"string-constant";
NSString * __nullable const DBConstantsOptStringConstant = @"string-constant";
DBConstantRecord * __nonnull const DBConstantsObjectConstant = [[DBConstantRecord alloc] initWithSomeInteger:DBConstantsI32Constant
someString:DBConstantsStringConstant];
BOOL const DBConstantsDummy = NO;
#pragma clang diagnostic pop
@implementation DBConstants
- (nonnull instancetype)init
......@@ -59,6 +38,55 @@ BOOL const DBConstantsDummy = NO;
return [[self alloc] init];
}
+ (NSNumber * __nullable)optBoolConstant
{
static NSNumber * const s_optBoolConstant = @YES;
return s_optBoolConstant;
}
+ (NSNumber * __nullable)optI8Constant
{
static NSNumber * const s_optI8Constant = @1;
return s_optI8Constant;
}
+ (NSNumber * __nullable)optI16Constant
{
static NSNumber * const s_optI16Constant = @2;
return s_optI16Constant;
}
+ (NSNumber * __nullable)optI32Constant
{
static NSNumber * const s_optI32Constant = @3;
return s_optI32Constant;
}
+ (NSNumber * __nullable)optI64Constant
{
static NSNumber * const s_optI64Constant = @4;
return s_optI64Constant;
}
+ (NSNumber * __nullable)optF32Constant
{
static NSNumber * const s_optF32Constant = @5.0;
return s_optF32Constant;
}
+ (NSNumber * __nullable)optF64Constant
{
static NSNumber * const s_optF64Constant = @5.0;
return s_optF64Constant;
}
+ (DBConstantRecord * __nonnull)objectConstant
{
static DBConstantRecord * const s_objectConstant = [[DBConstantRecord alloc] initWithSomeInteger:DBConstantsI32Constant
someString:DBConstantsStringConstant];
return s_objectConstant;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ %p>", self.class, (void *)self];
......
......@@ -36,6 +36,56 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSNumber * __nullable)optBoolConstant
{
static NSNumber * const s_optBoolConstant = @YES;
return s_optBoolConstant;
}
+ (NSNumber * __nullable)optI8Constant
{
static NSNumber * const s_optI8Constant = @1;
return s_optI8Constant;
}
+ (NSNumber * __nullable)optI16Constant
{
static NSNumber * const s_optI16Constant = @2;
return s_optI16Constant;
}
+ (NSNumber * __nullable)optI32Constant
{
static NSNumber * const s_optI32Constant = @3;
return s_optI32Constant;
}
+ (NSNumber * __nullable)optI64Constant
{
static NSNumber * const s_optI64Constant = @4;
return s_optI64Constant;
}
+ (NSNumber * __nullable)optF32Constant
{
static NSNumber * const s_optF32Constant = @5.0;
return s_optF32Constant;
}
+ (NSNumber * __nullable)optF64Constant
{
static NSNumber * const s_optF64Constant = @5.0;
return s_optF64Constant;
}
+ (DBConstantRecord * __nonnull)objectConstant
{
static DBConstantRecord * const s_objectConstant = [[DBConstantRecord alloc] initWithSomeInteger:DBConstantsInterfaceI32Constant
someString:DBConstantsInterfaceStringConstant];
return s_objectConstant;
}
namespace djinni_generated {
auto ConstantsInterface::toCpp(ObjcType objc) -> CppType
......
......@@ -17,22 +17,8 @@ extern int32_t const DBConstantsInterfaceI32Constant;
extern int64_t const DBConstantsInterfaceI64Constant;
extern float const DBConstantsInterfaceF32Constant;
extern double const DBConstantsInterfaceF64Constant;
extern NSNumber * __nullable const DBConstantsInterfaceOptBoolConstant;
extern NSNumber * __nullable const DBConstantsInterfaceOptI8Constant;
/** opt_i16_constant has documentation. */
extern NSNumber * __nullable const DBConstantsInterfaceOptI16Constant;
extern NSNumber * __nullable const DBConstantsInterfaceOptI32Constant;
extern NSNumber * __nullable const DBConstantsInterfaceOptI64Constant;
/**
* opt_f32_constant has long documentation.
* (Second line of multi-line documentation.
* Indented third line of multi-line documentation.)
*/
extern NSNumber * __nullable const DBConstantsInterfaceOptF32Constant;
extern NSNumber * __nullable const DBConstantsInterfaceOptF64Constant;
extern NSString * __nonnull const DBConstantsInterfaceStringConstant;
extern NSString * __nullable const DBConstantsInterfaceOptStringConstant;
extern DBConstantRecord * __nonnull const DBConstantsInterfaceObjectConstant;
/** Interface containing constants */
@interface DBConstantsInterface : NSObject
......@@ -44,4 +30,18 @@ extern DBConstantRecord * __nonnull const DBConstantsInterfaceObjectConstant;
*/
- (void)dummy;
+ (NSNumber * __nullable)optBoolConstant;
+ (NSNumber * __nullable)optI8Constant;
/** opt_i16_constant has documentation. */
+ (NSNumber * __nullable)optI16Constant;
+ (NSNumber * __nullable)optI32Constant;
+ (NSNumber * __nullable)optI64Constant;
/**
* opt_f32_constant has long documentation.
* (Second line of multi-line documentation.
* Indented third line of multi-line documentation.)
*/
+ (NSNumber * __nullable)optF32Constant;
+ (NSNumber * __nullable)optF64Constant;
+ (DBConstantRecord * __nonnull)objectConstant;
@end
......@@ -3,8 +3,6 @@
#import "DBConstantsInterface.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
BOOL const DBConstantsInterfaceBoolConstant = YES;
......@@ -20,25 +18,6 @@ float const DBConstantsInterfaceF32Constant = 5.0f;
double const DBConstantsInterfaceF64Constant = 5.0;
NSNumber * __nullable const DBConstantsInterfaceOptBoolConstant = @YES;
NSNumber * __nullable const DBConstantsInterfaceOptI8Constant = @1;
NSNumber * __nullable const DBConstantsInterfaceOptI16Constant = @2;
NSNumber * __nullable const DBConstantsInterfaceOptI32Constant = @3;
NSNumber * __nullable const DBConstantsInterfaceOptI64Constant = @4;
NSNumber * __nullable const DBConstantsInterfaceOptF32Constant = @5.0;
NSNumber * __nullable const DBConstantsInterfaceOptF64Constant = @5.0;
NSString * __nonnull const DBConstantsInterfaceStringConstant = @"string-constant";
NSString * __nullable const DBConstantsInterfaceOptStringConstant = @"string-constant";
DBConstantRecord * __nonnull const DBConstantsInterfaceObjectConstant = [[DBConstantRecord alloc] initWithSomeInteger:DBConstantsInterfaceI32Constant
someString:DBConstantsInterfaceStringConstant];
#pragma clang diagnostic pop
......@@ -9,8 +9,8 @@
- (nonnull instancetype)initWithFoo:(BOOL)foo;
+ (nonnull instancetype)extendedRecordWithFoo:(BOOL)foo;
+ (DBExtendedRecord * __nonnull)extendedRecordConst;
@property (nonatomic, readonly) BOOL foo;
@end
extern DBExtendedRecord * __nonnull const DBExtendedRecordExtendedRecordConst;
......@@ -3,12 +3,6 @@
#import "DBExtendedRecord.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
DBExtendedRecord * __nonnull const DBExtendedRecordExtendedRecordConst = [[DBExtendedRecord alloc] initWithFoo:YES];
#pragma clang diagnostic pop
@implementation DBExtendedRecord
......@@ -25,6 +19,12 @@ DBExtendedRecord * __nonnull const DBExtendedRecordExtendedRecordConst = [[DBExt
return [[self alloc] initWithFoo:foo];
}
+ (DBExtendedRecord * __nonnull)extendedRecordConst
{
static DBExtendedRecord * const s_extendedRecordConst = [[DBExtendedRecord alloc] initWithFoo:YES];
return s_extendedRecordConst;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ %p foo:%@>", self.class, (void *)self, @(self.foo)];
......
......@@ -13,6 +13,6 @@ public class MockRecordTest extends TestCase {
public void testMockConstants() {
Constants mock = new MockConstants();
assertEquals("The toString() method should be overrided.", "MockConstants{}", mock.toString());
assertEquals("The toString() method should be overridden.", "MockConstants{}", mock.toString());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment