1*65c59e02SInna Palant /* 2*65c59e02SInna Palant * Copyright (c) Facebook, Inc. and its affiliates. 3*65c59e02SInna Palant * 4*65c59e02SInna Palant * Licensed under the Apache License, Version 2.0 (the "License"); 5*65c59e02SInna Palant * you may not use this file except in compliance with the License. 6*65c59e02SInna Palant * You may obtain a copy of the License at 7*65c59e02SInna Palant * 8*65c59e02SInna Palant * http://www.apache.org/licenses/LICENSE-2.0 9*65c59e02SInna Palant * 10*65c59e02SInna Palant * Unless required by applicable law or agreed to in writing, software 11*65c59e02SInna Palant * distributed under the License is distributed on an "AS IS" BASIS, 12*65c59e02SInna Palant * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*65c59e02SInna Palant * See the License for the specific language governing permissions and 14*65c59e02SInna Palant * limitations under the License. 15*65c59e02SInna Palant */ 16*65c59e02SInna Palant 17*65c59e02SInna Palant package com.facebook.jni; 18*65c59e02SInna Palant 19*65c59e02SInna Palant import static org.assertj.core.api.Assertions.assertThat; 20*65c59e02SInna Palant 21*65c59e02SInna Palant import com.facebook.jni.annotations.DoNotStrip; 22*65c59e02SInna Palant import org.junit.Test; 23*65c59e02SInna Palant 24*65c59e02SInna Palant public class HybridTests extends BaseFBJniTests { 25*65c59e02SInna Palant static class TestHybridClass { 26*65c59e02SInna Palant // Hybrid classes must include a member which manages the C++ object. It 27*65c59e02SInna Palant // will be initialized from C++. It must be declared exactly with this 28*65c59e02SInna Palant // type and name, so JNI can find it, and initialized once in the ctor. 29*65c59e02SInna Palant // The annotation is necessary to keep proguard from renaming it, or else JNI 30*65c59e02SInna Palant // won't be able to find it. 31*65c59e02SInna Palant @DoNotStrip private final HybridData mHybridData; 32*65c59e02SInna Palant 33*65c59e02SInna Palant // This is the method which creates the C++ instance and initializes 34*65c59e02SInna Palant // mHybridData. Conventionally, it should be named initHybrid, and invoked 35*65c59e02SInna Palant // from the constructor. This must be called only once. If the C++ 36*65c59e02SInna Palant // instance is referenced before this is called, a NullPointerException 37*65c59e02SInna Palant // will be thrown. initHybrid(int i, String s, boolean b)38*65c59e02SInna Palant private native HybridData initHybrid(int i, String s, boolean b); 39*65c59e02SInna Palant 40*65c59e02SInna Palant // You can have more than one, which may be useful if the ctor is 41*65c59e02SInna Palant // overloaded. This will call the default C++ ctor. initHybrid()42*65c59e02SInna Palant private native HybridData initHybrid(); 43*65c59e02SInna Palant 44*65c59e02SInna Palant // Implements factory-style initialization. You shouldn't usually 45*65c59e02SInna Palant // need both styles in one class. Here we do it for testing and 46*65c59e02SInna Palant // demo purposes. initHybrid(String s, int i, boolean b)47*65c59e02SInna Palant private native HybridData initHybrid(String s, int i, boolean b); 48*65c59e02SInna Palant 49*65c59e02SInna Palant // Java ctor must invoke initHybrid(). This just passes arguments through, 50*65c59e02SInna Palant // but the ctor can do whatever work it wants, as long as it calls 51*65c59e02SInna Palant // initHybrid() before any native methods. TestHybridClass(int i, String s, boolean b)52*65c59e02SInna Palant public TestHybridClass(int i, String s, boolean b) { 53*65c59e02SInna Palant mHybridData = initHybrid(i, s, b); 54*65c59e02SInna Palant } 55*65c59e02SInna Palant 56*65c59e02SInna Palant // This behaves the same as the ctor above, I just wanted a different 57*65c59e02SInna Palant // signature to demonstrate factory-style initialization. TestHybridClass(String s, int i, boolean b)58*65c59e02SInna Palant public TestHybridClass(String s, int i, boolean b) { 59*65c59e02SInna Palant mHybridData = initHybrid(s, i, b); 60*65c59e02SInna Palant } 61*65c59e02SInna Palant 62*65c59e02SInna Palant // This is the simplest case. Even if everything is default, initHybrid() 63*65c59e02SInna Palant // must still be called. TestHybridClass()64*65c59e02SInna Palant public TestHybridClass() { 65*65c59e02SInna Palant mHybridData = initHybrid(); 66*65c59e02SInna Palant } 67*65c59e02SInna Palant 68*65c59e02SInna Palant // Java ctor used by C++ newObjectCxxArgs. Note this is private. TestHybridClass(HybridData hd)69*65c59e02SInna Palant private TestHybridClass(HybridData hd) { 70*65c59e02SInna Palant mHybridData = hd; 71*65c59e02SInna Palant } 72*65c59e02SInna Palant doneUsingIt()73*65c59e02SInna Palant public void doneUsingIt() { 74*65c59e02SInna Palant mHybridData.resetNative(); 75*65c59e02SInna Palant } 76*65c59e02SInna Palant 77*65c59e02SInna Palant // Some C++ methods. setBoth(int i, String s)78*65c59e02SInna Palant public native void setBoth(int i, String s); 79*65c59e02SInna Palant getInt()80*65c59e02SInna Palant public native int getInt(); 81*65c59e02SInna Palant getString()82*65c59e02SInna Palant public native String getString(); 83*65c59e02SInna Palant getCharString()84*65c59e02SInna Palant public native String getCharString(); 85*65c59e02SInna Palant copy1(TestHybridClass other)86*65c59e02SInna Palant public native boolean copy1(TestHybridClass other); 87*65c59e02SInna Palant copy2(TestHybridClass other)88*65c59e02SInna Palant public native boolean copy2(TestHybridClass other); 89*65c59e02SInna Palant oops()90*65c59e02SInna Palant public native void oops(); 91*65c59e02SInna Palant setGlobal(String s)92*65c59e02SInna Palant public native void setGlobal(String s); 93*65c59e02SInna Palant getGlobal1()94*65c59e02SInna Palant public native String getGlobal1(); 95*65c59e02SInna Palant getGlobal2()96*65c59e02SInna Palant public native String getGlobal2(); 97*65c59e02SInna Palant makeWithTwo()98*65c59e02SInna Palant public static native TestHybridClass makeWithTwo(); 99*65c59e02SInna Palant makeWithThree()100*65c59e02SInna Palant public static native TestHybridClass makeWithThree(); 101*65c59e02SInna Palant autoconvertMany()102*65c59e02SInna Palant public static native void autoconvertMany(); 103*65c59e02SInna Palant } 104*65c59e02SInna Palant 105*65c59e02SInna Palant @Test testHybridClass()106*65c59e02SInna Palant public void testHybridClass() { 107*65c59e02SInna Palant TestHybridClass thc1 = new TestHybridClass(); 108*65c59e02SInna Palant assertThat(thc1.getInt()).isEqualTo(0); 109*65c59e02SInna Palant assertThat(thc1.getString()).isEqualTo(""); 110*65c59e02SInna Palant 111*65c59e02SInna Palant thc1.setBoth(1, "one"); 112*65c59e02SInna Palant assertThat(thc1.getInt()).isEqualTo(1); 113*65c59e02SInna Palant assertThat(thc1.getString()).isEqualTo("one"); 114*65c59e02SInna Palant 115*65c59e02SInna Palant TestHybridClass thc2 = TestHybridClass.makeWithTwo(); 116*65c59e02SInna Palant assertThat(thc2.getInt()).isEqualTo(2); 117*65c59e02SInna Palant assertThat(thc2.getString()).isEqualTo("two"); 118*65c59e02SInna Palant 119*65c59e02SInna Palant thc2.doneUsingIt(); 120*65c59e02SInna Palant 121*65c59e02SInna Palant thrown.expect(NullPointerException.class); 122*65c59e02SInna Palant thc2.getInt(); 123*65c59e02SInna Palant } 124*65c59e02SInna Palant 125*65c59e02SInna Palant @Test testHybridAutoconversion()126*65c59e02SInna Palant public void testHybridAutoconversion() { 127*65c59e02SInna Palant TestHybridClass thc3 = TestHybridClass.makeWithThree(); 128*65c59e02SInna Palant assertThat(thc3.copy1(new TestHybridClass(3, "three", false))).isTrue(); 129*65c59e02SInna Palant assertThat(thc3.getInt()).isEqualTo(3); 130*65c59e02SInna Palant assertThat(thc3.getString()).isEqualTo("three"); 131*65c59e02SInna Palant 132*65c59e02SInna Palant TestHybridClass thc4 = new TestHybridClass(); 133*65c59e02SInna Palant thc4.copy1(new TestHybridClass("four", 4, false)); 134*65c59e02SInna Palant assertThat(thc4.getInt()).isEqualTo(4); 135*65c59e02SInna Palant assertThat(thc4.getString()).isEqualTo("four"); 136*65c59e02SInna Palant assertThat(thc4.getCharString()).isEqualTo("four"); 137*65c59e02SInna Palant 138*65c59e02SInna Palant TestHybridClass thc5 = new TestHybridClass(); 139*65c59e02SInna Palant assertThat(thc5.copy2(new TestHybridClass(5, "five", false))).isTrue(); 140*65c59e02SInna Palant assertThat(thc5.getInt()).isEqualTo(5); 141*65c59e02SInna Palant assertThat(thc5.getString()).isEqualTo("five"); 142*65c59e02SInna Palant } 143*65c59e02SInna Palant 144*65c59e02SInna Palant @Test testReturnGlobalRef()145*65c59e02SInna Palant public void testReturnGlobalRef() { 146*65c59e02SInna Palant TestHybridClass thc = new TestHybridClass(); 147*65c59e02SInna Palant thc.setGlobal("global_ref"); 148*65c59e02SInna Palant assertThat(thc.getGlobal1()).isEqualTo("global_ref"); 149*65c59e02SInna Palant assertThat(thc.getGlobal2()).isEqualTo("global_ref"); 150*65c59e02SInna Palant } 151*65c59e02SInna Palant 152*65c59e02SInna Palant @Test testLocalLeak()153*65c59e02SInna Palant public void testLocalLeak() { 154*65c59e02SInna Palant TestHybridClass.autoconvertMany(); 155*65c59e02SInna Palant } 156*65c59e02SInna Palant 157*65c59e02SInna Palant @Test testExceptionMapping()158*65c59e02SInna Palant public void testExceptionMapping() { 159*65c59e02SInna Palant TestHybridClass thc1 = new TestHybridClass(); 160*65c59e02SInna Palant thrown.expect(ArrayStoreException.class); 161*65c59e02SInna Palant thc1.oops(); 162*65c59e02SInna Palant } 163*65c59e02SInna Palant 164*65c59e02SInna Palant abstract static class AbstractTestHybrid { 165*65c59e02SInna Palant @DoNotStrip private final HybridData mHybridData; 166*65c59e02SInna Palant 167*65c59e02SInna Palant private int mAbstractNum; 168*65c59e02SInna Palant AbstractTestHybrid(HybridData hybridData, int an)169*65c59e02SInna Palant protected AbstractTestHybrid(HybridData hybridData, int an) { 170*65c59e02SInna Palant mHybridData = hybridData; 171*65c59e02SInna Palant mAbstractNum = an; 172*65c59e02SInna Palant } 173*65c59e02SInna Palant abstractNum()174*65c59e02SInna Palant public int abstractNum() { 175*65c59e02SInna Palant return mAbstractNum; 176*65c59e02SInna Palant } 177*65c59e02SInna Palant nativeNum()178*65c59e02SInna Palant public native int nativeNum(); 179*65c59e02SInna Palant concreteNum()180*65c59e02SInna Palant public abstract int concreteNum(); 181*65c59e02SInna Palant sum()182*65c59e02SInna Palant public abstract int sum(); 183*65c59e02SInna Palant } 184*65c59e02SInna Palant 185*65c59e02SInna Palant static class ConcreteTestHybrid extends AbstractTestHybrid { ConcreteTestHybrid(int an, int nn, int cn)186*65c59e02SInna Palant public ConcreteTestHybrid(int an, int nn, int cn) { 187*65c59e02SInna Palant super(initHybrid(nn, cn), an); 188*65c59e02SInna Palant } 189*65c59e02SInna Palant initHybrid(int nn, int cn)190*65c59e02SInna Palant private static native HybridData initHybrid(int nn, int cn); 191*65c59e02SInna Palant 192*65c59e02SInna Palant // overrides can be native 193*65c59e02SInna Palant @Override concreteNum()194*65c59e02SInna Palant public native int concreteNum(); 195*65c59e02SInna Palant 196*65c59e02SInna Palant // overrides can be java 197*65c59e02SInna Palant @Override sum()198*65c59e02SInna Palant public int sum() { 199*65c59e02SInna Palant return nativeNum() + abstractNum() + concreteNum(); 200*65c59e02SInna Palant } 201*65c59e02SInna Palant } 202*65c59e02SInna Palant 203*65c59e02SInna Palant @Test testHybridInheritance()204*65c59e02SInna Palant public void testHybridInheritance() { 205*65c59e02SInna Palant AbstractTestHybrid ath = new ConcreteTestHybrid(1, 2, 3); 206*65c59e02SInna Palant assertThat(ath.abstractNum()).isEqualTo(1); 207*65c59e02SInna Palant assertThat(ath.nativeNum()).isEqualTo(2); 208*65c59e02SInna Palant assertThat(ath.concreteNum()).isEqualTo(3); 209*65c59e02SInna Palant assertThat(ath.sum()).isEqualTo(6); 210*65c59e02SInna Palant } 211*65c59e02SInna Palant cxxTestInheritance(AbstractTestHybrid ath)212*65c59e02SInna Palant public static native boolean cxxTestInheritance(AbstractTestHybrid ath); 213*65c59e02SInna Palant makeAbstractHybrid()214*65c59e02SInna Palant public static native AbstractTestHybrid makeAbstractHybrid(); 215*65c59e02SInna Palant 216*65c59e02SInna Palant @Test testHybridCxx()217*65c59e02SInna Palant public void testHybridCxx() { 218*65c59e02SInna Palant AbstractTestHybrid ath = new ConcreteTestHybrid(4, 5, 6); 219*65c59e02SInna Palant assertThat(cxxTestInheritance(ath)).isTrue(); 220*65c59e02SInna Palant 221*65c59e02SInna Palant AbstractTestHybrid ath2 = makeAbstractHybrid(); 222*65c59e02SInna Palant assertThat(ath2 instanceof ConcreteTestHybrid).isTrue(); 223*65c59e02SInna Palant assertThat(ath2.abstractNum()).isEqualTo(7); 224*65c59e02SInna Palant assertThat(ath2.nativeNum()).isEqualTo(8); 225*65c59e02SInna Palant assertThat(ath2.concreteNum()).isEqualTo(9); 226*65c59e02SInna Palant assertThat(ath2.sum()).isEqualTo(24); 227*65c59e02SInna Palant } 228*65c59e02SInna Palant 229*65c59e02SInna Palant static class Base {} 230*65c59e02SInna Palant 231*65c59e02SInna Palant static class Derived extends Base { 232*65c59e02SInna Palant @DoNotStrip private final HybridData mHybridData; 233*65c59e02SInna Palant Derived(HybridData hybridData)234*65c59e02SInna Palant private Derived(HybridData hybridData) { 235*65c59e02SInna Palant mHybridData = hybridData; 236*65c59e02SInna Palant } 237*65c59e02SInna Palant } 238*65c59e02SInna Palant cxxTestDerivedJavaClass()239*65c59e02SInna Palant public static native boolean cxxTestDerivedJavaClass(); 240*65c59e02SInna Palant 241*65c59e02SInna Palant @Test testDerivedJavaClassCxx()242*65c59e02SInna Palant public void testDerivedJavaClassCxx() { 243*65c59e02SInna Palant assertThat(cxxTestDerivedJavaClass()).isTrue(); 244*65c59e02SInna Palant } 245*65c59e02SInna Palant 246*65c59e02SInna Palant static class TestHybridClassBase extends HybridClassBase { initHybrid()247*65c59e02SInna Palant protected native void initHybrid(); 248*65c59e02SInna Palant initHybrid(int i)249*65c59e02SInna Palant private native void initHybrid(int i); 250*65c59e02SInna Palant TestHybridClassBase()251*65c59e02SInna Palant protected TestHybridClassBase() { 252*65c59e02SInna Palant // No initHybrid() here! 253*65c59e02SInna Palant // Otherwise factory construction will set native pointer twice and process will crash. 254*65c59e02SInna Palant } 255*65c59e02SInna Palant TestHybridClassBase(int i)256*65c59e02SInna Palant public TestHybridClassBase(int i) { 257*65c59e02SInna Palant initHybrid(i); 258*65c59e02SInna Palant } 259*65c59e02SInna Palant 260*65c59e02SInna Palant // Some C++ methods. setInt(int i)261*65c59e02SInna Palant public native void setInt(int i); 262*65c59e02SInna Palant getInt()263*65c59e02SInna Palant public native int getInt(); 264*65c59e02SInna Palant makeWithThree()265*65c59e02SInna Palant public static native TestHybridClassBase makeWithThree(); 266*65c59e02SInna Palant } 267*65c59e02SInna Palant 268*65c59e02SInna Palant static class TestHybridClassBaseDefaultCtor extends TestHybridClassBase { TestHybridClassBaseDefaultCtor()269*65c59e02SInna Palant public TestHybridClassBaseDefaultCtor() { 270*65c59e02SInna Palant initHybrid(); 271*65c59e02SInna Palant } 272*65c59e02SInna Palant } 273*65c59e02SInna Palant 274*65c59e02SInna Palant @Test testHybridBaseDefaultCtor()275*65c59e02SInna Palant public void testHybridBaseDefaultCtor() { 276*65c59e02SInna Palant TestHybridClassBaseDefaultCtor base = new TestHybridClassBaseDefaultCtor(); 277*65c59e02SInna Palant assertThat(base.getInt()).isZero(); 278*65c59e02SInna Palant 279*65c59e02SInna Palant base.setInt(58); 280*65c59e02SInna Palant assertThat(base.getInt()).isEqualTo(58); 281*65c59e02SInna Palant } 282*65c59e02SInna Palant 283*65c59e02SInna Palant @Test testHybridBaseConstructorArgs()284*65c59e02SInna Palant public void testHybridBaseConstructorArgs() { 285*65c59e02SInna Palant TestHybridClassBase base = new TestHybridClassBase(42); 286*65c59e02SInna Palant assertThat(base.getInt()).isEqualTo(42); 287*65c59e02SInna Palant } 288*65c59e02SInna Palant 289*65c59e02SInna Palant @Test testHybridBaseFactoryConstruction()290*65c59e02SInna Palant public void testHybridBaseFactoryConstruction() { 291*65c59e02SInna Palant TestHybridClassBase base = TestHybridClassBase.makeWithThree(); 292*65c59e02SInna Palant assertThat(base.getInt()).isEqualTo(3); 293*65c59e02SInna Palant } 294*65c59e02SInna Palant 295*65c59e02SInna Palant static class Destroyable { 296*65c59e02SInna Palant @DoNotStrip private final HybridData mHybridData; 297*65c59e02SInna Palant Destroyable(HybridData hybridData)298*65c59e02SInna Palant private Destroyable(HybridData hybridData) { 299*65c59e02SInna Palant mHybridData = hybridData; 300*65c59e02SInna Palant } 301*65c59e02SInna Palant } 302*65c59e02SInna Palant cxxTestHybridDestruction()303*65c59e02SInna Palant public static native boolean cxxTestHybridDestruction(); 304*65c59e02SInna Palant 305*65c59e02SInna Palant @Test testHybridDestuction()306*65c59e02SInna Palant public void testHybridDestuction() { 307*65c59e02SInna Palant assertThat(cxxTestHybridDestruction()).isTrue(); 308*65c59e02SInna Palant } 309*65c59e02SInna Palant } 310