@Override
	public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
			throws CancelledException {
		AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
		if (!asciiEnabled && !unicodeEnabled && !subroutinesEnabled) {
			String message = "ASCII, Unicode, and Subroutines are all disabled.";
			log.appendMsg(getName(), message);
			log.setStatus(message);

			return false;
		}

		processorAlignment = program.getLanguage().getInstructionAlignment();

		newCodeFound = false;
		int count = NOTIFICATION_INTERVAL;
		long initial_count = set.getNumAddresses();
		monitor.initialize(initial_count);
		AddressSet leftSet = new AddressSet(set);

		// Iterate over all references within the new address set
		//   Evaluate each reference
		//
		Listing listing = program.getListing();
		Memory memory = program.getMemory();

		AddressSet executeSet = getExecuteSet(memory);
		if (!respectExecuteFlags) {
			executeSet = null;
		}
		AddressSet ignoreNewPointers = new AddressSet();

		AddressIterator iter = program.getReferenceManager().getReferenceSourceIterator(set, true);
		PseudoDisassembler pdis = new PseudoDisassembler(program);
		pdis.setRespectExecuteFlag(respectExecuteFlags);
		monitor.setMessage("Analyze Operand References " + set.getMinAddress());

		AddressSet disTargets = new AddressSet();
		AddressSet foundCodeBookmarkLocations = new AddressSet();

		AddressSet doneSubTest = new AddressSet();

		// set of targets already checked.  Don't do them again.
		AddressSet checkedTargets = new AddressSet();

		while (iter.hasNext() && !newCodeFound) {
			monitor.checkCancelled();

			Address addr = iter.next();

			count++;
			if (count > NOTIFICATION_INTERVAL) {
				leftSet.deleteRange(leftSet.getMinAddress(), addr);
				monitor.setProgress(initial_count - leftSet.getNumAddresses());
				monitor.setMessage("Analyze OpRefs : " + addr);
				count = 0;
			}

			if (ignoreNewPointers.contains(addr)) {
				continue;
			}
			CodeUnit cu = listing.getCodeUnitContaining(addr);
			if (cu == null) {
				continue;
			}
//TODO: should really make a list of places to disassemble code
//      and another list of places that read/write the contents of
//      the memory location.  Then at the end schedule disassembly
//      of those places that are not on the read/write list.

			// check out any memory references
			//   for code references or valid strings
			//

			Reference[] memRefs = cu.getReferencesFrom();
			// ignore any references coming out of here, they are all about to be processed
			ignoreNewPointers.addRange(cu.getMinAddress(), cu.getMaxAddress());
			for (int m = 0; m < memRefs.length && !monitor.isCancelled(); m++) {
				Reference reference = memRefs[m];
				Address target = reference.getToAddress();

				RefType memRefType = reference.getReferenceType();
				if (memRefType.isFlow() && !memRefType.isIndirect() && !(cu instanceof Data)) {
					if (memRefType.isCall() && memRefType.isComputed()) {
						Function func = listing.getFunctionAt(target);
						if (func == null) {
							// better make sure function analyzer sees this...
							// TODO: this is probably too much callusion.
							FunctionAnalyzer anal = new FunctionAnalyzer();
							AddressSet funcSet = new AddressSet(reference.getFromAddress());
							mgr.scheduleOneTimeAnalysis(anal, funcSet);
						}
					}

					if (memRefType.isJump()) {
						checkForExternalJump(program, reference, monitor);
					}

					Instruction instr = (Instruction) cu;

					// if is a computed jump reference, this could be a thunk
					if (memRefType.isComputed() && memRefs.length <= 2) {
						if (memRefType.isCall() &&
							instr.getFlowType() != RefType.COMPUTED_CALL_TERMINATOR) {
							continue;
						}
						FunctionManager funcMgr = program.getFunctionManager();
						Function func = funcMgr.getFunctionContaining(reference.getFromAddress());
						if (func != null) {
							// this could be a thunk, force a re-analysis.
							// single instruction with a computed jump on it.
							//    New information from the thunked function (noreturn, callfixup, etc...)
							//    may affect callers to the function, so tell analyzers about it.
							// TODO: this should be done by the Auto Thunking mechanisms...
							if ((!func.isThunk() &&
								CreateThunkFunctionCmd.isThunk(program, func))) {
								CreateFunctionCmd createFunctionCmd = new CreateFunctionCmd(null,
									func.getEntryPoint(), null, SourceType.ANALYSIS, false, true);
								if (createFunctionCmd.applyTo(program)) {
									AutoAnalysisManager amgr =
										AutoAnalysisManager.getAnalysisManager(program);
									amgr.functionDefined(
										new AddressSet(func.getEntryPoint(), func.getEntryPoint()));
									checkedTargets.delete(target, target); // take off set, so can check again
								}
							}
						}
					}
					continue;
				}

				// check for already looked at targets here, so if more pointers
				//    to a location are found (say jumps/calls to a location other than a data read/write
				//    The location will be checks
				if (checkedTargets.contains(target)) {
					continue;
				}
				checkedTargets.add(target);

				// if memory doesn't contain the target address
				if (!reference.isMemoryReference()) {
					continue;
				}
				if (!memory.contains(target)) {
					continue;
				}
				if (ignoreNewPointers.contains(target)) {
					continue;
				}

				// check if something is defined there
				// if it is a string, let is go through.
				boolean stuffDefined = false;
				boolean isUndefinedStuff = false;
				Data data = listing.getDefinedDataContaining(target);
				if (data != null) {
					DataType dt = data.getDataType();
					stuffDefined = true;
					if (!(dt instanceof StringDataType || data.isPointer())) {
						if (dt instanceof Undefined) {
							isUndefinedStuff = true;
						}
						else {
							continue;
						}
					}
				}
				else {
					Instruction targetInstr = listing.getInstructionContaining(target);
					if (targetInstr != null) {
						doneSubTest.addRange(targetInstr.getMinAddress(),
							targetInstr.getMaxAddress());
						// if this is a computed instruction, keep analyzing it
						if (cu instanceof Instruction) {
							if (!((Instruction) cu).getFlowType().isComputed()) {
								if (shouldBeValidFunction(program, targetInstr)) {
									// it is already disassembled, but not a function yet.
									disTargets.addRange(target, target);
								}
								continue;
							}
						}
						else {
							if (shouldBeValidFunction(program, targetInstr)) {
								// it is already disassembled, but not a function yet.
								disTargets.addRange(target, target);
							}
							continue;
						}
					}
				}

				// check if it could be code
//				if (memRefs[m].isUserDefined()) {
				Instruction instr = null;
				if (cu instanceof Instruction) {
					instr = (Instruction) cu;
				}

				if (instr != null) {
					AddressTable table = getAddressTable(program, instr,
						reference.getOperandIndex(), target, monitor);
					if (table != null) {
						if (table.getNumberAddressEntries() >= minimumAddressTableSize) {
							createFlowTable(program, instr, reference.getOperandIndex(), table,
								monitor);
							// if new code found, must yield analysis elsewhere
							if (newCodeFound) {
								leftSet = new AddressSet(set);
								leftSet.deleteRange(leftSet.getMinAddress(), addr);
								leftSet.addRange(addr, addr);
								break;
							}
							continue;
						}
					}
				}

				// TODO: decide whether we should look for code that is a potential address table.
				//
				if (subroutinesEnabled && /** !potentialAddressTable && **/
					!isUndefinedStuff && !doneSubTest.contains(target)) {
					RefType refType = reference.getReferenceType();
					// only check references that aren't marked as read/write
					if (!(refType.isRead() || refType.isWrite() ||
						hasDataAccessReferences(program, target))) {
						// assume someone else will handle this correctly.  We clearly can't here
						if (instr != null &&
							(instr.getFlowType().isJump() || instr.getFlowType().isCall())) {
							continue;
						}

						// if not an instruction, and value not used for a calculation
						if (instr == null || !isUsedForCalculation(instr, target)) {
							Address fromAddress = reference.getFromAddress();
							// only check in code marked executable if there is any
							if (executeSet == null || executeSet.contains(target) ||
								isFunctionPointer(listing, fromAddress)) {
								doneSubTest.addRange(target, target);
								Symbol sym = program.getSymbolTable().getSymbol(reference);

								// only allow code to fall into other code when subroutine on a data item
								if (pdis.isValidSubroutine(target, instr == null)) {
									// if this came from code, mark it as found code
									if (instr != null) {
										foundCodeBookmarkLocations.addRange(target, target);
									}
									// THIS IS a HACK.  Don't check address table locations.
									//   something else already is doing this.  Too much bad code is being found

									if (sym == null || !sym.getName().startsWith("AddrTable")) {
										disTargets.addRange(target, target);
									}
								}
							}
						}
					}
					else {
//							Err.debug(this, "is read/write at " + memRefs[m].getFromAddress());
					}
				}
//				}
				if (stuffDefined && !isUndefinedStuff) {
					continue;
				}

				if (asciiEnabled && checkForAscii(program, pdis, target)) {
					continue;
				}

				if (unicodeEnabled && checkForUnicode(program, pdis, target)) {
					continue;
				}

				// TODO: Maybe this should check for valid pointers, even if it was identified good code.
				if (pointerEnabled && !disTargets.contains(target) &&
					checkForPointer(program, pdis, target, true)) {
					data = program.getListing().getDefinedDataAt(target);
					if (data != null && data.isPointer()) {
						Address ptrAddr = data.getAddress(0);
						ignoreNewPointers.addRange(ptrAddr, ptrAddr);
					}
					continue;
				}
			}
		}

		// don't create functions where there is data
		AddressIterator aiter = disTargets.getAddresses(true);
		AddressSet throwOutSet = new AddressSet();
		AddressSet doneDisSet = new AddressSet();
		while (aiter.hasNext()) {
			Address addr = aiter.next();
			Data data = program.getListing().getDataContaining(addr);
			if (data != null) {
				if (data.isDefined()) {
					throwOutSet.add(addr);
				}
				continue;
			}
			Instruction instr = program.getListing().getInstructionContaining(addr);
			if (instr != null) {
				doneDisSet.add(addr);
			}
		}
		disTargets = disTargets.subtract(throwOutSet);

		if (!disTargets.isEmpty()) {
			// TODO: delayed disassembly should check if the code starts are still valid
			AddressSet doDisTargets = disTargets.subtract(doneDisSet);
			if (!doDisTargets.isEmpty()) {
				BackgroundCommand<Program> cmd =
					createDisassemblyCommandsForAddress(program, doDisTargets);
				mgr.schedule(cmd, AnalysisPriority.REFERENCE_ANALYSIS.after().after().priority());
			}

			createFunctions(program, disTargets);
			foundCodeBookmarkLocations = foundCodeBookmarkLocations.subtract(throwOutSet);
			AddressIterator foundIter = foundCodeBookmarkLocations.getAddresses(true);
			while (foundIter.hasNext()) {
				Address target = foundIter.next();
				program.getBookmarkManager()
						.setBookmark(target, BookmarkType.ANALYSIS, "Found Code",
							"Found code from operand reference");
			}
		}

		// Set up a one time analysis to get us back into here if
		//   there are still addresses on the set
		//
		if (newCodeFound && !leftSet.isEmpty()) {
			mgr.scheduleOneTimeAnalysis(this, leftSet);
		}
		return true;
	}
Check operand references to memory locations looking for
Data
Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/OperandReferenceAnalyzer.java