/******************************************************************************* * Copyright (c) 2008, 2016 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.debug.ui.model; import java.math.BigInteger; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.tcf.debug.ui.ITCFPrettyExpressionProvider; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.services.IExpressions; import org.eclipse.tcf.services.IExpressions.Value; import org.eclipse.tcf.services.ISymbols; import org.eclipse.tcf.services.ISymbols.Symbol; import org.eclipse.tcf.util.TCFDataCache; public class TCFChildrenSubExpressions extends TCFChildren { private final int par_level; private final int par_offs; private final int par_size; TCFChildrenSubExpressions(TCFNode node, int par_level, int par_offs, int par_size) { super(node, 128); this.par_level = par_level; this.par_offs = par_offs; this.par_size = par_size; } void onSuspended(boolean func_call) { reset(); for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onSuspended(func_call); if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onSuspended(func_call); } } void onValueChanged() { reset(); for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onValueChanged(); if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onValueChanged(); } } void onRegisterValueChanged() { reset(); for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onRegisterValueChanged(); if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onRegisterValueChanged(); } } void onMemoryChanged() { reset(); for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryChanged(); if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryChanged(); } } void onMemoryMapChanged() { reset(); for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryMapChanged(); if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryMapChanged(); } } void onCastToTypeChanged() { cancel(); TCFNode a[] = getNodes().toArray(new TCFNode[getNodes().size()]); for (int i = 0; i < a.length; i++) a[i].dispose(); } TCFNodeExpression getField(String field_id, boolean deref) { assert field_id != null; for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) { TCFNodeExpression e = (TCFNodeExpression)n; if (field_id.equals(e.getFieldID()) && e.isDeref() == deref) return e; } } if (isValid()) return null; TCFNodeExpression e = new TCFNodeExpression(node, null, null, field_id, null, null, -1, deref); add(e); return e; } private boolean findFields(ISymbols.Symbol type, Map<String,TCFNode> map, boolean deref) { TCFDataCache<String[]> children_cache = node.model.getSymbolChildrenCache(type.getID()); if (children_cache == null) return true; if (!children_cache.validate(this)) return false; String[] children = children_cache.getData(); if (children == null) return true; TCFDataCache<?> pending = null; for (String id : children) { TCFDataCache<ISymbols.Symbol> sym_cache = node.model.getSymbolInfoCache(id); if (!sym_cache.validate()) { pending = sym_cache; } else { ISymbols.Symbol sym_data = sym_cache.getData(); if (sym_data == null) continue; switch (sym_data.getSymbolClass()) { case reference: if (sym_data.getFlag(ISymbols.SYM_FLAG_ARTIFICIAL)) continue; if (sym_data.getName() == null && !sym_data.getFlag(ISymbols.SYM_FLAG_INHERITANCE)) { if (!findFields(sym_data, map, deref)) return false; } else { TCFNodeExpression n = getField(id, deref); n.setSortPosition(map.size()); map.put(n.id, n); } break; case variant: if (!findFields(sym_data, map, deref)) return false; break; case variant_part: if (node.model.isFilterVariantsByDiscriminant()) { // find discriminant by offset String discr_id = null; int offset = sym_data.getOffset(); for (String id2 : children) { if (id.equals(id2)) continue; TCFDataCache<ISymbols.Symbol> discr_sym_cache = node.model.getSymbolInfoCache(id2); if (!discr_sym_cache.validate()) { pending = discr_sym_cache; continue; } Symbol discr_sym_data = discr_sym_cache.getData(); if (discr_sym_data == null) continue; if (discr_sym_data.getSymbolClass() == ISymbols.SymbolClass.variant_part) continue; if (discr_sym_data.getOffset() == offset) { discr_id = id2; break; } } if (discr_id == null) continue; // filter variants by discriminant value if (!filterVariants(sym_data, map, discr_id, deref)) return false; } else { if (!findFields(sym_data, map, deref)) return false; } break; default: break; } } } if (pending == null) return true; pending.wait(this); return false; } private boolean filterVariants(ISymbols.Symbol type, Map<String,TCFNode> map, String discr_id, boolean deref) { TCFNodeExpression discr_expr = getField(discr_id, deref); TCFDataCache<Value> discr_value_cache = discr_expr.getValue(); if (!discr_value_cache.validate(this)) return false; Value discr_value = discr_value_cache.getData(); if (discr_value == null) return true; BigInteger discr = TCFNumberFormat.toBigInteger(discr_value.getValue(), discr_value.isBigEndian(), false); TCFDataCache<String[]> children_cache = node.model.getSymbolChildrenCache(type.getID()); if (children_cache == null) return true; if (!children_cache.validate(this)) return false; String[] children = children_cache.getData(); if (children == null) return true; TCFDataCache<?> pending = null; ISymbols.Symbol variant_sym_data = null; ISymbols.Symbol default_sym_data = null; for (String id : children) { TCFDataCache<ISymbols.Symbol> sym_cache = node.model.getSymbolInfoCache(id); if (!sym_cache.validate()) { pending = sym_cache; } else { ISymbols.Symbol sym_data = sym_cache.getData(); if (sym_data == null) continue; switch (sym_data.getSymbolClass()) { case variant: TCFDataCache<Map<String,Object>> sym_loc_cache = node.model.getSymbolLocationCache(id); if (!sym_loc_cache.validate()) { pending = sym_loc_cache; continue; } Map<String,Object> sym_loc_data = sym_loc_cache.getData(); Object discr_info = sym_loc_data.get("Discriminant"); if (discr_info instanceof List) { List<?> values = (List<?>) discr_info; for (Object value : values) { if (value instanceof Number) { BigInteger val = JSON.toBigInteger((Number) value); if (discr.equals(val)) { variant_sym_data = sym_data; break; } } else if (value instanceof Map) { Map<?,?> range = (Map<?,?>) value; Object x = range.get("X"); Object y = range.get("Y"); if (!(x instanceof Number) || !(y instanceof Number)) continue; BigInteger lower = JSON.toBigInteger((Number) x); BigInteger upper = JSON.toBigInteger((Number) y); if (discr.compareTo(lower) >= 0 && discr.compareTo(upper) <= 0) { variant_sym_data = sym_data; break; } } } } else if (discr_info == null) { default_sym_data = sym_data; } break; default: assert false : "Unexpected symbol class: " + sym_data.getSymbolClass(); break; } } if (variant_sym_data != null) break; } if (pending != null) { pending.wait(this); return false; } if (variant_sym_data == null) variant_sym_data = default_sym_data; if (variant_sym_data != null) { if (!findFields(variant_sym_data, map, deref)) return false; } return true; } private TCFNodeExpression findReg(String reg_id) { assert reg_id != null; for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) { TCFNodeExpression e = (TCFNodeExpression)n; if (reg_id.equals(e.getRegisterID())) return e; } } return null; } private boolean findRegs(TCFNodeRegister reg_node, Map<String,TCFNode> map) { TCFChildren reg_children = reg_node.getChildren(); if (!reg_children.validate(this)) return false; for (TCFNode subnode : reg_children.toArray()) { TCFNodeExpression n = findReg(subnode.id); if (n == null) add(n = new TCFNodeExpression(node, null, null, null, null, subnode.id, -1, false)); n.setSortPosition(map.size()); map.put(n.id, n); } return true; } private TCFNodeExpression findIndex(int index, boolean deref) { assert index >= 0; for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) { TCFNodeExpression e = (TCFNodeExpression)n; if (e.getIndex() == index && e.isDeref() == deref) return e; } } return null; } private TCFNodeArrayPartition findPartition(int offs, int size) { assert offs >= 0; for (TCFNode n : getNodes()) { if (n instanceof TCFNodeArrayPartition) { TCFNodeArrayPartition e = (TCFNodeArrayPartition)n; if (e.getOffset() == offs && e.getSize() == size) return e; } } return null; } private TCFNodeExpression findScript(String s) { // TODO: need faster search for (TCFNode n : getNodes()) { if (n instanceof TCFNodeExpression) { TCFNodeExpression e = (TCFNodeExpression)n; if (s.equals(e.getScript())) return e; } } return null; } @Override protected boolean startDataRetrieval() { assert !isDisposed(); TCFNode exp = node; while (!(exp instanceof TCFNodeExpression)) exp = exp.parent; for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) { TCFDataCache<String[]> c = p.getChildren(exp); if (c != null) { if (!c.validate(this)) return false; if (c.getError() == null && c.getData() != null) { int i = 0; HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); for (String s : c.getData()) { TCFNodeExpression n = findScript(s); if (n == null) n = new TCFNodeExpression(node, s, null, null, null, null, -1, false); n.setSortPosition(i++); data.put(n.id, n); } set(null, null, data); return true; } } } TCFDataCache<ISymbols.Symbol> type_cache = ((TCFNodeExpression)exp).getType(); if (!type_cache.validate(this)) return false; ISymbols.Symbol type_data = type_cache.getData(); if (type_data == null) { HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue(); if (!val_cache.validate(this)) return false; IExpressions.Value val_data = val_cache.getData(); if (val_data != null) { String reg_id = val_data.getRegisterID(); if (reg_id != null) { if (!node.model.createNode(reg_id, this)) return false; if (isValid()) return true; TCFNodeRegister reg_node = (TCFNodeRegister)node.model.getNode(reg_id); if (!findRegs(reg_node, data)) return false; } } set(null, null, data); return true; } ISymbols.TypeClass type_class = type_data.getTypeClass(); Map<String,TCFNode> data = new HashMap<String,TCFNode>(); if (par_level > 0 && type_class != ISymbols.TypeClass.array) { // Nothing } else if (type_class == ISymbols.TypeClass.composite) { if (!findFields(type_data, data, false)) return false; } else if (type_class == ISymbols.TypeClass.array) { int offs = par_level > 0 ? par_offs : 0; int size = par_level > 0 ? par_size : type_data.getLength(); if (size <= 100) { for (int i = offs; i < offs + size; i++) { TCFNodeExpression n = findIndex(i, false); if (n == null) n = new TCFNodeExpression(node, null, null, null, null, null, i, false); n.setSortPosition(i); data.put(n.id, n); } } else { int next_size = 100; while (size / next_size > 100) next_size *= 100; for (int i = offs; i < offs + size; i += next_size) { int sz = next_size; if (i + sz > offs + size) sz = offs + size - i; TCFNodeArrayPartition n = findPartition(i, sz); if (n == null) n = new TCFNodeArrayPartition(node, par_level + 1, i, sz); data.put(n.id, n); } } } else if (type_class == ISymbols.TypeClass.pointer) { TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue(); if (!val_cache.validate(this)) return false; IExpressions.Value val_data = val_cache.getData(); if (val_data != null && (val_data.isImplicitPointer() || !isNull(val_data.getValue()))) { TCFDataCache<ISymbols.Symbol> base_type_cache = node.model.getSymbolInfoCache(type_data.getBaseTypeID()); if (base_type_cache != null) { if (!base_type_cache.validate(this)) return false; ISymbols.Symbol base_type_data = base_type_cache.getData(); if (base_type_data != null && base_type_data.getTypeClass() != ISymbols.TypeClass.function && base_type_data.getSize() > 0) { if (base_type_data.getTypeClass() == ISymbols.TypeClass.composite) { if (!findFields(base_type_data, data, true)) return false; } else { TCFNodeExpression n = findIndex(0, true); if (n == null) n = new TCFNodeExpression(node, null, null, null, null, null, 0, true); n.setSortPosition(0); data.put(n.id, n); } } } } } set(null, null, data); return true; } private boolean isNull(byte[] data) { if (data == null) return true; for (byte b : data) { if (b != 0) return false; } return true; } }