/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.bridge; import com.facebook.infer.annotation.Assertions; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.config.ReactFeatureFlags; import java.util.ArrayList; import java.util.Arrays; import javax.annotation.Nullable; /** * Implementation of a NativeArray that allows read-only access to its members. This will generally * be constructed and filled in native code so you shouldn't construct one yourself. */ @DoNotStrip public class ReadableNativeArray extends NativeArray implements ReadableArray { static { ReactBridge.staticInit(); } protected ReadableNativeArray(HybridData hybridData) { super(hybridData); } //WriteOnce but not in the constructor fields private @Nullable Object[] mLocalArray; private @Nullable ReadableType[] mLocalTypeArray; private static int jniPassCounter = 0; public static void setUseNativeAccessor(boolean useNativeAccessor) { ReactFeatureFlags.useArrayNativeAccessor = useNativeAccessor; } public static int getJNIPassCounter() { return jniPassCounter; } private Object[] getLocalArray() { // Fast, non blocking check for the common case if (mLocalArray != null) { return mLocalArray; } synchronized (this) { // Make sure no concurrent call already updated if (mLocalArray == null) { jniPassCounter++; mLocalArray = Assertions.assertNotNull(importArray()); } } return mLocalArray; } private native Object[] importArray(); private ReadableType[] getLocalTypeArray() { // Fast, non-blocking check for the common case if (mLocalTypeArray != null) { return mLocalTypeArray; } synchronized (this) { // Make sure no concurrent call already updated if (mLocalTypeArray == null) { jniPassCounter++; Object[] tempArray = Assertions.assertNotNull(importTypeArray()); mLocalTypeArray = Arrays.copyOf(tempArray, tempArray.length, ReadableType[].class); } } return mLocalTypeArray; } private native Object[] importTypeArray(); @Override public int size() { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return sizeNative(); } return getLocalArray().length; } private native int sizeNative(); @Override public boolean isNull(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return isNullNative(index); } return getLocalArray()[index] == null; } private native boolean isNullNative(int index); @Override public boolean getBoolean(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getBooleanNative(index); } return ((Boolean) getLocalArray()[index]).booleanValue(); } private native boolean getBooleanNative(int index); @Override public double getDouble(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getDoubleNative(index); } return ((Double) getLocalArray()[index]).doubleValue(); } private native double getDoubleNative(int index); @Override public int getInt(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getIntNative(index); } return ((Double) getLocalArray()[index]).intValue(); } private native int getIntNative(int index); @Override public String getString(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getStringNative(index); } return (String) getLocalArray()[index]; } private native String getStringNative(int index); @Override public ReadableNativeArray getArray(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getArrayNative(index); } return (ReadableNativeArray) getLocalArray()[index]; } private native ReadableNativeArray getArrayNative(int index); @Override public ReadableNativeMap getMap(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getMapNative(index); } return (ReadableNativeMap) getLocalArray()[index]; } private native ReadableNativeMap getMapNative(int index); @Override public ReadableType getType(int index) { if (ReactFeatureFlags.useArrayNativeAccessor) { jniPassCounter++; return getTypeNative(index); } return getLocalTypeArray()[index]; } private native ReadableType getTypeNative(int index); @Override public Dynamic getDynamic(int index) { return DynamicFromArray.create(this, index); } @Override public ArrayList toArrayList() { ArrayList arrayList = new ArrayList<>(); for (int i = 0; i < this.size(); i++) { switch (getType(i)) { case Null: arrayList.add(null); break; case Boolean: arrayList.add(getBoolean(i)); break; case Number: arrayList.add(getDouble(i)); break; case String: arrayList.add(getString(i)); break; case Map: arrayList.add(getMap(i).toHashMap()); break; case Array: arrayList.add(getArray(i).toArrayList()); break; default: throw new IllegalArgumentException("Could not convert object at index: " + i + "."); } } return arrayList; } }