Pilfer / heresy

Inspect and instrument React Native applications at runtime
MIT License
33 stars 0 forks source link

React Native Elements #3

Open razaina opened 1 week ago

razaina commented 1 week ago

Hi,

i tried to play a bit with react-native-elements.ts as I was trying to understand how to get access to a specific property of a JSX.Element.

I am not very familiar with React-native so I thought it would help me to learn more about it.

Let's say I have this code.

interface MyContainerProps {
  onJobDone: (something: string) => void;
}

export function DoSomething({ onJobDone }: MyContainerProps): JSX.Element {

  const [currentJobState, isDoingJob] = useState(JobState.doit);

  const { dataA, dataB } = initStuff();

  useEffect(() => {
    if (currentJobState !== JobState.finish) return;
    onJobDone(dataA);
  }, [currentJobState]); 

  return (
    <>
        [...]
          <JobView dataA={dataA} isDoingJob={isDoingJob}  />
         [...]
    </>
  );
}

I want to get access to dataA or isDoingJob from JobView, here is the injected JS code:

const objKeys = (item) => {
    if(item && item !== undefined && item !== null){
        return Object.keys(item)
    }else{
        return "null"
    }
}

const hook_react_native_elements = () => {
    log('[*] Inspecting React Native Elements via Map.set hook');
    const originalMapSet = Map.prototype.set;
    Map.prototype.set = function (key, value) {
        try {
            if (value && value.type) {
                inspectElements(value);
            }
        } catch (error) {
            log(error)
        }
        return originalMapSet.call(this, key, value);
    };
}
const inspectElements = (value) => {
  let name = '';
  if (value && value !== undefined && value !== null) {

    if (value.type) name = getTagName(value) || '';
    log("NAME " + name)

    if (name === 'RCTView') {
      let child = value.return;
      let childName = getTagName(child.return);
      log('childName  ' + childName);
      if(childName === 'JobView'){
        log("       " + objKeys(child.return))

        log("       tag: " + child.return.tag)
        log("       key: " + child.return.key)
        log("       elementType: " + child.return.elementType)
        log("       type: " + child.return.type)
        log("       stateNode: " + child.return.stateNode)
        log("       return: " + objKeys(child.return.return))

        log("       child: " + objKeys(child.return.child))
        log("       sibling: " + child.return.sibling)
        log("       index: " + child.return.index)
        log("       ref: " + child.return.ref)
        log("       refCleanup: " + child.return.refCleanup)
        log("       pendingProps: " + objKeys(child.return.pendingProps))
        log("           pendingProps.dataA:" + (child.return.pendingProps.dataA))
        log("           pendingProps.dataA:" + objKeys(child.return.pendingProps.dataA))
        log("       memoizedProps: " + objKeys(child.return.memoizedProps))
        log("           memoizedProps.dataA:" + (child.return.memoizedProps.dataA))
        log("           memoizedProps.dataA:" + objKeys(child.return.memoizedProps.dataA))
        log("       memoizedState: " + child.return.memoizedState)
        log("       dependencies: " + child.return.dependencies)
        log("       mode: " + child.return.mode)
        log("       flags: " + child.return.flags)
        log("       subTreeFlags: " + child.return.subTreeFlags)
        log("       deletions: " + child.return.deletions)
        log("       lanes: " + child.return.lanes)
        log("       childLanes: " + child.return.childLanes)
        log("       alternate: " + child.return.alternate)

      }
    }
  }
};

The output:

ReactNativeJS: [+] childName  DoSomething                                                                                                                        │  │
ReactNativeJS: [+]        tag,key,elementType,type,stateNode,return,child,sibling,index,ref,refCleanup,pendingProps,memoizedProps,updateQueue,memoizedState,dependencies,mode,flags,subtreeF │  │
 │  │ lags,deletions,lanes,childLanes,alternate                                                                                                                                                                                     │  │
ReactNativeJS: [+]        tag: 0                                                                                                                                                             │  │
ReactNativeJS: [+]        key: .0                                                                                                                                                            │  │
ReactNativeJS: [+]        elementType: function JobView(a0) { [bytecode] }                                                                                   │  │
ReactNativeJS: [+]        type: function JobView(a0) { [bytecode] }                                                                                          │  │
ReactNativeJS: [+]        stateNode: null                                                                                                                                                    │  │
ReactNativeJS: [+]        return: tag,key,elementType,type,stateNode,return,child,sibling,index,ref,refCleanup,pendingProps,memoizedProps,updateQueue,memoizedState,dependencies,mode,flags, │  │
 │  │ subtreeFlags,deletions,lanes,childLanes,alternate                                                                                                                                                                             │  │
ReactNativeJS: [+]        child: tag,key,elementType,type,stateNode,return,child,sibling,index,ref,refCleanup,pendingProps,memoizedProps,updateQueue,memoizedState,dependencies,mode,flags,s │  │
 │  │ ubtreeFlags,deletions,lanes,childLanes,alternate                                                                                                                                                                              │  │
ReactNativeJS: [+]        sibling: null                                                                                                                                                      │  │
ReactNativeJS: [+]        index: 0                                                                                                                                                           │  │
ReactNativeJS: [+]        ref: null                                                                                                                                                         ││  │
ReactNativeJS: [+]        refCleanup: null                                                                                                                                                  ││  │
ReactNativeJS: [+]        pendingProps: dataA,isDoingJob                                                                                                                        ││  │
ReactNativeJS: [+]            pendingProps.dataA:                                                                                                                                        ││  │
ReactNativeJS: [+]            pendingProps.dataA:null                                                                                                                                     │  │
ReactNativeJS: [+]        memoizedProps: dataA,isDoingJob                                                                                                                        │  │
ReactNativeJS: [+]            memoizedProps.dataA:                                                                                                                                        │  │
ReactNativeJS: [+]            memoizedProps.dataA:null                                                                                                                                    │  │
ReactNativeJS: [+]        memoizedState: null                                                                                                                                                │  │
ReactNativeJS: [+]        dependencies: null                                                                                                                                                 │  │
ReactNativeJS: [+]        mode: 0                                                                                                                                                            │  │
ReactNativeJS: [+]        flags: 1                                                                                                                                                           │  │
ReactNativeJS: [+]        subTreeFlags: undefined                                                                                                                                            │  │
ReactNativeJS: [+]        deletions: null                                                                                                                                                    │  │
ReactNativeJS: [+]        lanes: 0                                                                                                                                                           │  │
ReactNativeJS: [+]        childLanes: 0                                                                                                                                                      │  │
ReactNativeJS: [+]        alternate: null

How come dataA is "null" ? I don't really know what i am missing here. Do you have any idea how to reach dataA ?

Pilfer commented 1 week ago

I'm not entirely sure why they'd be null - perhaps it has something to do with the fact that pendingProps is pre-render and memoizedProps is post-render.

Is there a chance that the element you're targeting isn't updated to reflect the actual values yet? I'd add more logging and see if you can find a fully-populated instance of it elsewhere in the tree - or even at a different time (like after a navigation event or something else that triggers a Fiber re-render). At first glance I'd assume that what you've posted here is the first instance of the rendered element, before the props have been assigned and updated.

FWIW I'm working on HBC merging in hermes_rs right now, and plan on building an API that allows people to hook specific functions with the bytecode itself. When that's done, hooking React.createElement will be fairly trivial and we'll be able to access direct references to the objects. I'll try to update this issue when I have something working!

Let me know if you figure it out, btw!