blob: 233033c39c1d494ecb9ff88ebf47632d3b42214b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2021 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 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.debug.core.IExpressionManager;
import org.eclipse.debug.core.model.IExpression;
import org.eclipse.debug.core.model.IWatchExpression;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tcf.debug.ui.ITCFExpression;
import org.eclipse.tcf.debug.ui.ITCFPrettyExpressionProvider;
import org.eclipse.tcf.internal.debug.actions.TCFAction;
import org.eclipse.tcf.internal.debug.model.TCFContextState;
import org.eclipse.tcf.internal.debug.ui.Activator;
import org.eclipse.tcf.internal.debug.ui.ColorCache;
import org.eclipse.tcf.internal.debug.ui.ImageCache;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.JSON;
import org.eclipse.tcf.services.IExpressions;
import org.eclipse.tcf.services.IMemory;
import org.eclipse.tcf.services.IMemory.MemoryError;
import org.eclipse.tcf.services.IRegisters;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.util.TCFDataCache;
import org.eclipse.tcf.util.TCFTask;
public class TCFNodeExpression extends TCFNode implements IElementEditor, ICastToType,
IWatchInExpressions, IDetailsProvider, ITCFExpression {
// TODO: User commands: Add Global Variables, Remove Global Variables
// TODO: enable Change Value user command
private final String script;
private final IExpression platform_expression;
private final String field_id;
private final String reg_id;
private final int index;
private final boolean deref;
private final TCFData<String> base_text;
private final TCFData<IExpressions.Expression> var_expression;
private final TCFData<IExpressions.Expression> rem_expression;
private final TCFData<IExpressions.Value> value;
private final TCFData<ISymbols.Symbol> type;
private final TCFData<String> type_name;
private final TCFData<StyledStringBuffer> string;
private final TCFData<String> expression_text;
private final TCFChildrenSubExpressions children;
private final boolean is_empty;
private int base_text_priority = 0;
private int expr_text_priority = 0;
private int sort_pos;
private boolean enabled = true;
private IExpressions.Value prev_value;
private IExpressions.Value next_value;
private byte[] parent_value;
private String remote_expression_id;
private Object update_generation;
private static int expr_cnt;
private final static int max_type_chain_length = 256;
private final Runnable post_delta = new Runnable() {
@Override
public void run() {
postAllChangedDelta();
}
};
TCFNodeExpression(final TCFNode parent, String expression_script, IExpression platform_expression,
final String field_id, final String var_id, final String reg_id,
final int index, final boolean deref) {
super(parent, var_id != null ? var_id : "Expr" + expr_cnt++);
if (platform_expression == null) {
this.script = expression_script;
this.platform_expression = null;
}
else {
this.script = platform_expression.getExpressionText();
this.platform_expression = platform_expression;
}
this.field_id = field_id;
this.reg_id = reg_id;
this.index = index;
this.deref = deref;
is_empty = script == null && var_id == null && field_id == null && reg_id == null && index < 0 && !deref;
var_expression = new TCFData<IExpressions.Expression>(channel) {
@Override
protected boolean startDataRetrieval() {
IExpressions exps = launch.getService(IExpressions.class);
if (exps == null || var_id == null) {
set(null, null, null);
return true;
}
command = exps.getContext(var_id, new IExpressions.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, IExpressions.Expression context) {
set(token, error, context);
}
});
return false;
}
};
base_text = new TCFData<String>(channel) {
@Override
protected boolean startDataRetrieval() {
/* Compute expression script, not including type cast */
base_text_priority = 0;
parent_value = null;
int expr_priority = 0;
String expr_script = null;
Throwable expr_error = null;
if (is_empty) {
expr_script = null;
}
else if (script != null) {
expr_script = script;
if (script.matches("\\w*")) expr_priority = 2;
}
else if (var_id != null) {
if (!var_expression.validate(this)) return false;
String exp = null;
if (var_expression.getData() == null) {
expr_error = var_expression.getError();
}
else {
exp = var_expression.getData().getExpression();
if (exp == null) expr_error = new Exception("Missing 'Expression' property");
else if (exp.matches("\\w*")) expr_priority = 2;
}
expr_script = exp;
}
else if (reg_id != null) {
expr_script = "${" + reg_id + "}";
expr_priority = 2;
}
else {
TCFNode n = parent;
while (n instanceof TCFNodeArrayPartition) n = n.parent;
String cast = model.getCastToType(n.id);
if (cast == null && deref) {
TCFNodeExpression exp = (TCFNodeExpression)n;
if (!exp.value.validate(this)) return false;
IExpressions.Value v = exp.value.getData();
if (v != null && v.getTypeID() != null) {
parent_value = v.getValue();
if (parent_value != null) {
expr_script = "(${" + v.getTypeID() + "})0x" + TCFNumberFormat.toBigInteger(
parent_value, v.isBigEndian(), false).toString(16);
expr_priority = 1;
}
}
}
if (expr_script == null) {
TCFDataCache<String> t = ((TCFNodeExpression)n).base_text;
if (!t.validate(this)) return false;
expr_script = t.getData();
if (expr_script == null) {
set(null, t.getError(), null);
return true;
}
expr_priority = ((TCFNodeExpression)n).base_text_priority;
}
if (cast != null) {
if (expr_priority < 1) expr_script = "(" + expr_script + ")";
expr_script = "(" + cast + ")" + expr_script;
expr_priority = 1;
}
if (field_id != null) {
if (expr_priority < 2) expr_script = "(" + expr_script + ")";
expr_script = expr_script + (deref ? "->" : ".") + "${" + field_id + "}";
expr_priority = 2;
}
else if (index >= 0) {
BigInteger lower_bound = getLowerBound(this);
if (lower_bound == null) return false;
if (expr_priority < 2) expr_script = "(" + expr_script + ")";
expr_script = expr_script + "[" + lower_bound.add(BigInteger.valueOf(index)) + "]";
expr_priority = 2;
}
else if (deref) {
if (expr_priority < 1) expr_script = "(" + expr_script + ")";
expr_script = "*" + expr_script;
expr_priority = 1;
}
}
base_text_priority = expr_priority;
set(null, expr_error, expr_script);
return true;
}
};
expression_text = new TCFData<String>(channel) {
@Override
protected boolean startDataRetrieval() {
/* Compute human readable expression script,
* including type cast, and using variable names instead of IDs */
expr_text_priority = 0;
int expr_priority = 0;
String expr_script = null;
if (is_empty) {
expr_script = null;
}
else if (script != null) {
expr_script = script;
if (expr_script.matches("\\w*")) expr_priority = 2;
}
else if (var_id != null) {
if (!var_expression.validate(this)) return false;
IExpressions.Expression e = var_expression.getData();
if (e != null) {
TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(e.getSymbolID());
if (var != null) {
if (!var.validate(this)) return false;
if (var.getData() != null) {
expr_script = var.getData().getName();
if (expr_script.matches("\\w*")) expr_priority = 2;
}
}
}
}
else if (reg_id != null) {
if (!model.createNode(reg_id, this)) return false;
if (isValid()) return true;
TCFNodeRegister reg_node = (TCFNodeRegister)model.getNode(reg_id);
for (;;) {
TCFDataCache<IRegisters.RegistersContext> ctx_cache = reg_node.getContext();
if (!ctx_cache.validate(this)) return false;
IRegisters.RegistersContext ctx_data = ctx_cache.getData();
if (ctx_data == null) {
set(null, ctx_cache.getError(), null);
return true;
}
expr_script = expr_script == null ? ctx_data.getName() : ctx_data.getName() + "." + expr_script;
if (!(reg_node.parent instanceof TCFNodeRegister)) break;
reg_node = (TCFNodeRegister)reg_node.parent;
}
expr_script = "$" + expr_script;
expr_priority = 2;
}
else {
TCFNode n = parent;
while (n instanceof TCFNodeArrayPartition) n = n.parent;
TCFDataCache<?> pending = null;
TCFDataCache<String> parent_text_cache = ((TCFNodeExpression)n).expression_text;
TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id);
if (!parent_text_cache.validate()) pending = parent_text_cache;
if (field != null && !field.validate()) pending = field;
if (!base_text.validate()) pending = base_text;
if (pending != null) {
pending.wait(this);
return false;
}
expr_script = parent_text_cache.getData();
expr_priority = ((TCFNodeExpression)n).expr_text_priority;
if (field != null) {
ISymbols.Symbol field_data = field.getData();
if (field_data != null) {
if (field_data.getName() != null) {
if (expr_priority < 2) expr_script = "(" + expr_script + ")";
expr_script = expr_script + (deref ? "->" : ".") + field_data.getName();
expr_priority = 2;
}
else if (field_data.getFlag(ISymbols.SYM_FLAG_INHERITANCE)) {
TCFDataCache<ISymbols.Symbol> type = model.getSymbolInfoCache(field_data.getTypeID());
if (type != null) {
if (!type.validate(this)) return false;
ISymbols.Symbol type_data = type.getData();
if (type_data != null) {
String type_name = type_data.getName();
if (expr_priority < 1) expr_script = "(" + expr_script + ")";
expr_script = "*(" + type_name + "*)" + (deref ? "" : "&") + expr_script;
expr_priority = 1;
}
}
}
}
}
else if (index >= 0) {
BigInteger lower_bound = getLowerBound(this);
if (lower_bound == null) return false;
if (expr_priority < 2) expr_script = "(" + expr_script + ")";
expr_script = expr_script + "[" + lower_bound.add(BigInteger.valueOf(index)) + "]";
expr_priority = 2;
}
else if (deref) {
if (expr_priority < 1) expr_script = "(" + expr_script + ")";
expr_script = "*" + expr_script;
expr_priority = 1;
}
if (expr_script == null && base_text.getData() != null) {
expr_script = base_text.getData();
expr_priority = base_text_priority;
}
}
if (expr_script != null) {
String cast = model.getCastToType(id);
if (cast != null) {
if (expr_priority < 1) expr_script = "(" + expr_script + ")";
expr_script = "(" + cast + ")(" + expr_script + ")";
expr_priority = 1;
}
}
expr_text_priority = expr_priority;
set(null, null, expr_script);
return true;
}
};
rem_expression = new TCFData<IExpressions.Expression>(channel) {
@Override
protected boolean startDataRetrieval() {
IExpressions exps = launch.getService(IExpressions.class);
if (exps == null) {
set(null, null, null);
return true;
}
String cast = model.getCastToType(id);
if (var_id != null && cast == null) {
if (!var_expression.validate(this)) return false;
set(null, var_expression.getError(), var_expression.getData());
return true;
}
if (!base_text.validate(this)) return false;
String e = base_text.getData();
if (e == null) {
set(null, base_text.getError(), null);
return true;
}
if (cast != null) e = "(" + cast + ")(" + e + ")";
TCFNode n = getRootExpression().parent;
if (n instanceof TCFNodeStackFrame && ((TCFNodeStackFrame)n).isEmulated()) n = n.parent;
command = exps.create(n.id, null, e, new IExpressions.DoneCreate() {
public void doneCreate(IToken token, Exception error, IExpressions.Expression context) {
disposeRemoteExpression();
if (context != null) remote_expression_id = context.getID();
if (!isDisposed()) set(token, error, context);
else disposeRemoteExpression();
}
});
return false;
}
};
value = new TCFData<IExpressions.Value>(channel) {
@Override
protected boolean startDataRetrieval() {
Boolean b = usePrevValue(this);
if (b == null) return false;
if (b) {
set(null, null, prev_value);
return true;
}
if (!rem_expression.validate(this)) return false;
final IExpressions.Expression exp = rem_expression.getData();
if (exp == null) {
set(null, rem_expression.getError(), null);
return true;
}
final TCFDataCache<?> cache = this;
IExpressions exps = launch.getService(IExpressions.class);
command = exps.evaluate(exp.getID(), new IExpressions.DoneEvaluate() {
public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) {
if (command != token) return;
command = null;
if (error != null) {
Boolean b = usePrevValue(cache);
if (b == null) return;
if (b) {
set(null, null, prev_value);
return;
}
}
set(null, error, value);
}
});
return false;
}
};
type = new TCFData<ISymbols.Symbol>(channel) {
@Override
protected boolean startDataRetrieval() {
String type_id = null;
if (model.getCastToType(id) == null && field_id != null) {
TCFDataCache<ISymbols.Symbol> sym_cache = model.getSymbolInfoCache(field_id);
if (sym_cache != null) {
if (!sym_cache.validate(this)) return false;
ISymbols.Symbol sym_data = sym_cache.getData();
if (sym_data != null) type_id = sym_data.getTypeID();
}
}
if (type_id == null) {
if (!value.validate(this)) return false;
IExpressions.Value val = value.getData();
if (val != null) type_id = val.getTypeID();
}
if (type_id == null) {
if (!rem_expression.validate(this)) return false;
IExpressions.Expression exp = rem_expression.getData();
if (exp != null) type_id = exp.getTypeID();
}
if (type_id == null) {
set(null, value.getError(), null);
return true;
}
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id);
if (type_cache == null) {
set(null, null, null);
return true;
}
if (!type_cache.validate(this)) return false;
set(null, type_cache.getError(), type_cache.getData());
return true;
}
};
string = new TCFData<StyledStringBuffer>(channel) {
ISymbols.Symbol base_type_data;
BigInteger addr;
byte[] buf;
int size;
int offs;
@Override
@SuppressWarnings("incomplete-switch")
protected boolean startDataRetrieval() {
if (addr != null) return continueMemRead();
if (!value.validate(this)) return false;
if (!type.validate(this)) return false;
IExpressions.Value value_data = value.getData();
ISymbols.Symbol type_data = type.getData();
if (value_data != null && value_data.getValue() != null && type_data != null) {
switch (type_data.getTypeClass()) {
case pointer:
case array:
TCFDataCache<ISymbols.Symbol> base_type_cache = model.getSymbolInfoCache(type_data.getBaseTypeID());
if (base_type_cache == null) break;
if (!base_type_cache.validate(this)) return false;
base_type_data = base_type_cache.getData();
if (base_type_data == null) break;
size = base_type_data.getSize();
if (size > 0x1000) break;
if (size <= 0) break;
switch (base_type_data.getTypeClass()) {
case integer:
case cardinal:
Boolean is_char = isCharType(base_type_data);
if (is_char == null) return false;
if (!is_char) break;
// c-string: read until character = 0
if (type_data.getTypeClass() == ISymbols.TypeClass.array) {
byte[] data = value_data.getValue();
StyledStringBuffer bf = new StyledStringBuffer();
bf.append(toASCIIString(data, 0, data.length, '"'), StyledStringBuffer.MONOSPACED);
set(null, null, bf);
return true;
}
// pointer, read c-string data from memory
size = 0; // read until 0
return startMemRead(value_data);
case composite:
if (type_data.getTypeClass() == ISymbols.TypeClass.array) break;
// pointer, read struct data from memory
return startMemRead(value_data);
}
break;
case integer:
case cardinal:
{
Boolean is_char = isCharType(type_data);
if (is_char == null) return false;
if (is_char) {
byte[] data = value_data.getValue();
if (data.length > 0) {
if (data.length > 1) {
BigInteger value_int = TCFNumberFormat.toBigInteger(
data, value_data.isBigEndian(), false);
if (value_int.compareTo(BigInteger.valueOf(0x80)) >= 0) break;
data = new byte[1];
data[0] = value_int.byteValue();
}
StyledStringBuffer bf = new StyledStringBuffer();
bf.append(toASCIIString(data, 0, 1, '\''), StyledStringBuffer.MONOSPACED);
set(null, null, bf);
return true;
}
}
}
break;
case enumeration:
TCFDataCache<String[]> type_children_cache = model.getSymbolChildrenCache(type_data.getID());
if (!type_children_cache.validate(this)) return false;
String[] type_children_data = type_children_cache.getData();
if (type_children_data == null) break;
BigInteger value_int = TCFNumberFormat.toBigInteger(
value_data.getValue(), value_data.isBigEndian(), false);
for (String const_id : type_children_data) {
TCFDataCache<ISymbols.Symbol> const_cache = model.getSymbolInfoCache(const_id);
if (!const_cache.validate(this)) return false;
ISymbols.Symbol const_data = const_cache.getData();
if (const_data != null && const_data.getName() != null && const_data.getValue() != null) {
BigInteger const_int = TCFNumberFormat.toBigInteger(
const_data.getValue(), const_data.isBigEndian(), false);
if (value_int.equals(const_int)) {
StyledStringBuffer bf = new StyledStringBuffer();
bf.append(const_data.getName());
set(null, null, bf);
return true;
}
}
}
break;
}
}
set(null, null, null);
return true;
}
@Override
public void reset() {
super.reset();
addr = null;
}
private Boolean isCharType(ISymbols.Symbol type) {
for (int i = 0; i < max_type_chain_length; i++) {
if (type == null) return false;
if (type.getSize() != 1) return false;
if (type.getProperties().get(ISymbols.PROP_BINARY_SCALE) != null) return false;
if (type.getProperties().get(ISymbols.PROP_DECIMAL_SCALE) != null) return false;
if (type.getFlag(ISymbols.SYM_FLAG_ARTIFICIAL)) return false;
String id = type.getTypeID();
if (id == null || id.equals(type.getID())) break;
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(id);
if (!type_cache.validate(this)) return null;
type = type_cache.getData();
}
return true;
}
private boolean startMemRead(IExpressions.Value value_data) {
byte[] data = value_data.getValue();
BigInteger a = TCFNumberFormat.toBigInteger(data, value_data.isBigEndian(), false);
if (!a.equals(BigInteger.valueOf(0))) {
addr = a;
offs = 0;
return continueMemRead();
}
set(null, null, null);
return true;
}
private boolean continueMemRead() {
// indirect value, need to read it from memory
TCFDataCache<TCFNodeExecContext> mem_node_cache = model.searchMemoryContext(parent);
if (mem_node_cache == null) {
set(null, new Exception("Context does not provide memory access"), null);
return true;
}
if (!mem_node_cache.validate(this)) return false;
if (mem_node_cache.getError() != null) {
set(null, mem_node_cache.getError(), null);
return true;
}
TCFNodeExecContext mem_node = mem_node_cache.getData();
if (mem_node == null) {
set(null, new Exception("Context does not provide memory access"), null);
return true;
}
TCFDataCache<IMemory.MemoryContext> mem_ctx_cache = mem_node.getMemoryContext();
if (!mem_ctx_cache.validate(this)) return false;
if (mem_ctx_cache.getError() != null) {
set(null, mem_ctx_cache.getError(), null);
return true;
}
IMemory.MemoryContext mem_space_data = mem_ctx_cache.getData();
if (mem_space_data == null) {
set(null, new Exception("Context does not provide memory access"), null);
return true;
}
if (size == 0) {
// c-string: read until 0
BigInteger get_addr = addr.add(BigInteger.valueOf(offs));
final int get_size = 16 - (get_addr.intValue() & 0xf);
if (buf == null) buf = new byte[256];
if (offs + get_size > buf.length) {
byte[] tmp = new byte[buf.length * 2];
System.arraycopy(buf, 0, tmp, 0, buf.length);
buf = tmp;
}
command = mem_space_data.get(get_addr, 1, buf, offs, get_size, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (command != token) return;
IMemory.ErrorOffset err_offs = null;
if (error instanceof IMemory.ErrorOffset) err_offs = (IMemory.ErrorOffset)error;
for (int i = 0; i < get_size; i++) {
MemoryError byte_error = null;
if (error != null && (err_offs == null || err_offs.getStatus(i) != IMemory.ErrorOffset.BYTE_VALID)) {
byte_error = error;
if (offs == 0) {
set(command, byte_error, null);
return;
}
}
if (buf[offs] == 0 || offs >= 2048 || byte_error != null) {
StyledStringBuffer bf = new StyledStringBuffer();
bf.append(toASCIIString(buf, 0, offs, '"'), StyledStringBuffer.MONOSPACED);
set(command, null, bf);
return;
}
offs++;
}
command = null;
run();
}
});
return false;
}
if (offs == 0) {
buf = new byte[size];
command = mem_space_data.get(addr, 1, buf, 0, size, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (error != null) {
set(command, error, null);
}
else if (command == token) {
command = null;
offs++;
run();
}
}
});
return false;
}
StyledStringBuffer bf = new StyledStringBuffer();
bf.append('{');
if (!appendCompositeValueText(bf, 1, base_type_data, null, TCFNodeExpression.this, true,
buf, 0, size, base_type_data.isBigEndian(), this)) return false;
bf.append('}');
set(null, null, bf);
return true;
}
};
type_name = new TCFData<String>(channel) {
@Override
protected boolean startDataRetrieval() {
if (!type.validate(this)) return false;
if (type.getData() != null) {
StringBuffer bf = new StringBuffer();
if (!getTypeName(bf, type, model.isShowQualifiedTypeNamesEnabled(), this)) return false;
set(null, null, bf.toString());
return true;
}
if (!value.validate(this)) return false;
IExpressions.Value val = value.getData();
if (val != null && val.getValue() != null) {
String s = getTypeName(val.getTypeClass(), val.getValue().length);
if (s != null) {
set(null, null, s);
return true;
}
}
if (!rem_expression.validate(this)) return false;
IExpressions.Expression exp = rem_expression.getData();
if (exp != null) {
String s = getTypeName(exp.getTypeClass(), exp.getSize());
if (s != null) {
set(null, null, s);
return true;
}
}
set(null, null, "N/A");
return true;
}
};
children = new TCFChildrenSubExpressions(this, 0, 0, 0);
}
void onPreferencesChanged() {
type_name.reset();
children.reset();
postAllChangedDelta();
}
private void disposeRemoteExpression() {
if (remote_expression_id != null && channel.getState() == IChannel.STATE_OPEN) {
IExpressions exps = channel.getRemoteService(IExpressions.class);
exps.dispose(remote_expression_id, new IExpressions.DoneDispose() {
public void doneDispose(IToken token, Exception error) {
if (error == null) return;
if (channel.getState() != IChannel.STATE_OPEN) return;
Activator.log("Error disposing remote expression evaluator", error);
}
});
remote_expression_id = null;
}
}
@Override
public void dispose() {
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.dispose(this);
disposeRemoteExpression();
super.dispose();
}
private TCFNodeExpression getRootExpression() {
TCFNode n = this;
while (n.parent instanceof TCFNodeExpression || n.parent instanceof TCFNodeArrayPartition) n = n.parent;
return (TCFNodeExpression)n;
}
private void postAllChangedDelta() {
TCFNodeExpression n = getRootExpression();
for (TCFModelProxy p : model.getModelProxies()) {
String id = p.getPresentationContext().getId();
if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id) && n.script != null ||
TCFModel.ID_EXPRESSION_HOVER.equals(id) && n.script != null ||
IDebugUIConstants.ID_VARIABLE_VIEW.equals(id) && n.script == null) {
p.addDelta(this, IModelDelta.STATE | IModelDelta.CONTENT);
}
}
}
private void resetBaseText() {
if (parent_value != null && base_text.isValid()) {
base_text.reset();
rem_expression.cancel();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
string.cancel();
value.cancel();
}
}
public void update(Object generation) {
if (update_generation == generation) return;
prev_value = null;
update_generation = generation;
if (rem_expression.isValid() && rem_expression.getError() != null) rem_expression.reset();
type.reset();
type_name.reset();
value.reset();
string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onSuspended(false);
resetBaseText();
}
void onSuspended(boolean func_call) {
update_generation = null;
if (!func_call) {
prev_value = next_value;
type.reset();
type_name.reset();
}
if (rem_expression.isValid() && rem_expression.getError() != null) rem_expression.reset();
if (!func_call || value.isValid() && value.getError() != null) value.reset();
if (!func_call || string.isValid() && string.getError() != null) string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onSuspended(func_call);
if (!func_call) resetBaseText();
// No need to post delta: parent posted CONTENT
}
void onRegisterValueChanged() {
prev_value = null;
value.reset();
type.reset();
type_name.reset();
string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onRegisterValueChanged();
resetBaseText();
postAllChangedDelta();
}
void onMemoryChanged() {
prev_value = null;
value.reset();
type.reset();
type_name.reset();
string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onMemoryChanged();
resetBaseText();
if (parent instanceof TCFNodeExpression) return;
if (parent instanceof TCFNodeArrayPartition) return;
postAllChangedDelta();
}
void onMemoryMapChanged() {
value.reset();
type.reset();
type_name.reset();
string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onMemoryMapChanged();
resetBaseText();
if (parent instanceof TCFNodeExpression) return;
if (parent instanceof TCFNodeArrayPartition) return;
postAllChangedDelta();
}
void onValueChanged() {
prev_value = next_value;
value.reset();
type.reset();
type_name.reset();
string.reset();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
children.onValueChanged();
resetBaseText();
postAllChangedDelta();
}
public void onCastToTypeChanged() {
rem_expression.cancel();
value.cancel();
type.cancel();
type_name.cancel();
string.cancel();
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) p.cancel(this);
expression_text.cancel();
children.onCastToTypeChanged();
resetBaseText();
postAllChangedDelta();
}
public boolean isEmpty() {
return is_empty;
}
public String getScript() {
return script;
}
public IExpression getPlatformExpression() {
return platform_expression;
}
String getFieldID() {
return field_id;
}
String getRegisterID() {
return reg_id;
}
int getIndex() {
return index;
}
boolean isDeref() {
return deref;
}
void setSortPosition(int sort_pos) {
this.sort_pos = sort_pos;
}
void setEnabled(boolean enabled) {
if (this.enabled == enabled) return;
this.enabled = enabled;
postAllChangedDelta();
}
public boolean isEnabled() {
return enabled;
}
/**
* Get expression properties cache that represents a variable.
* The cache is empty if the node does not represent a variable.
* @return The expression properties cache.
*/
public TCFDataCache<IExpressions.Expression> getVariable() {
return var_expression;
}
/**
* Get expression properties cache.
* If the node represents a variable, return same data same as getVariable().
* @return The expression properties cache.
*/
public TCFDataCache<IExpressions.Expression> getExpression() {
return rem_expression;
}
/**
* Get expression value cache.
* @return The expression value cache.
*/
public TCFDataCache<IExpressions.Value> getValue() {
return value;
}
/**
* Get expression type cache.
* @return The expression type cache.
*/
public TCFDataCache<ISymbols.Symbol> getType() {
return type;
}
/**
* Get human readable expression script,
* including type cast, and using variable names instead of IDs.
*/
public TCFDataCache<String> getExpressionText() {
return expression_text;
}
private Boolean usePrevValue(Runnable done) {
// Check if view should show old value.
// Old value is shown if context is running or
// stack trace does not contain expression parent frame.
// Return null if waiting for cache update.
if (prev_value == null) return false;
TCFNode p = getRootExpression().parent;
if (p instanceof TCFNodeStackFrame) {
TCFNodeExecContext exe = (TCFNodeExecContext)p.parent;
TCFAction action = model.getActiveAction(exe.id);
if (action != null && (action.showRunning() || model.getDelayStackUpdateUtilLastStep())) return true;
TCFDataCache<TCFContextState> state_cache = exe.getState();
if (!state_cache.validate(done)) return null;
TCFContextState state = state_cache.getData();
if (state == null || !state.is_suspended) return true;
TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace();
if (!stack_trace_cache.validate(done)) return null;
if (stack_trace_cache.getData().get(p.id) == null) return true;
}
else if (p instanceof TCFNodeExecContext) {
TCFNodeExecContext exe = (TCFNodeExecContext)p;
TCFAction action = model.getActiveAction(exe.id);
if (action != null && (action.showRunning() || model.getDelayStackUpdateUtilLastStep())) return true;
TCFDataCache<TCFContextState> state_cache = exe.getState();
if (!state_cache.validate(done)) return null;
TCFContextState state = state_cache.getData();
if (state == null || !state.is_suspended) return true;
}
return false;
}
@Override
void flushAllCaches() {
prev_value = null;
next_value = null;
super.flushAllCaches();
}
@SuppressWarnings("incomplete-switch")
private String getTypeName(ISymbols.TypeClass type_class, int size) {
switch (type_class) {
case integer:
if (size == 0) return "<Void>";
return "<Integer-" + (size * 8) + ">";
case cardinal:
if (size == 0) return "<Void>";
return "<Unsigned-" + (size * 8) + ">";
case real:
if (size == 0) return null;
return "<Float-" + (size * 8) + ">";
case complex:
if (size == 0) return null;
return "<Complex-" + (size / 2 * 8) + ">";
}
return null;
}
@SuppressWarnings("incomplete-switch")
private boolean getTypeName(StringBuffer bf, TCFDataCache<ISymbols.Symbol> type_cache, boolean qualified, Runnable done) {
String name = null;
boolean name_left = false;
for (int i = 0; i < max_type_chain_length; i++) {
String s = null;
boolean get_base_left = false;
boolean get_base_right = false;
if (!type_cache.validate(done)) return false;
ISymbols.Symbol type_symbol = type_cache.getData();
if (type_symbol != null) {
int flags = type_symbol.getFlags();
s = type_symbol.getName();
if (s != null) {
if (qualified && (flags & (ISymbols.SYM_FLAG_UNION_TYPE|ISymbols.SYM_FLAG_CLASS_TYPE|ISymbols.SYM_FLAG_STRUCT_TYPE|ISymbols.SYM_FLAG_ENUM_TYPE)) != 0) {
String prefix = getQualifiedTypeNamePrefix(type_symbol, done);
if (prefix == null) return false;
s = prefix + s;
}
if ((flags & ISymbols.SYM_FLAG_UNION_TYPE) != 0) s = "union " + s;
else if ((flags & ISymbols.SYM_FLAG_CLASS_TYPE) != 0) s = "class " + s;
else if ((flags & ISymbols.SYM_FLAG_INTERFACE_TYPE) != 0) s = "interface " + s;
else if ((flags & ISymbols.SYM_FLAG_STRUCT_TYPE) != 0) s = "struct " + s;
else if ((flags & ISymbols.SYM_FLAG_ENUM_TYPE) != 0) s = "enum " + s;
}
else if (!type_symbol.getID().equals(type_symbol.getTypeID())) {
// modified type without name, like "volatile int"
TCFDataCache<ISymbols.Symbol> base_type_cache = model.getSymbolInfoCache(type_symbol.getTypeID());
if (base_type_cache != null) {
StringBuffer sb = new StringBuffer();
if (!getTypeName(sb, base_type_cache, qualified, done)) return false;
s = sb.toString();
}
}
if (s == null) s = getTypeName(type_symbol.getTypeClass(), type_symbol.getSize());
if (s == null) {
switch (type_symbol.getTypeClass()) {
case pointer:
s = "*";
if ((flags & ISymbols.SYM_FLAG_REFERENCE) != 0) s = "&";
get_base_left = true;
break;
case member_pointer:
{
String id = type_symbol.getContainerID();
if (id != null) {
TCFDataCache<ISymbols.Symbol> cls_cache = model.getSymbolInfoCache(id);
if (!cls_cache.validate(done)) return false;
ISymbols.Symbol cls_data = cls_cache.getData();
if (cls_data != null) {
String cls_name = cls_data.getName();
if (cls_name != null) {
s = cls_name + "::*";
if (qualified) {
String prefix = getQualifiedTypeNamePrefix(cls_data, done);
if (prefix == null) return false;
s = prefix + s;
}
}
}
}
if (s == null) s = "::*";
}
get_base_left = true;
break;
case array:
s = "[" + type_symbol.getLength() + "]";
get_base_right = true;
break;
case composite:
s = "<Structure>";
break;
case function:
{
TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type_symbol.getID());
if (!children_cache.validate(done)) return false;
String[] children = children_cache.getData();
if (children != null) {
StringBuffer args = new StringBuffer();
if (name != null) {
args.append('(');
args.append(name);
args.append(')');
name = null;
}
args.append('(');
for (String id : children) {
if (id != children[0]) args.append(',');
if (!getTypeName(args, model.getSymbolInfoCache(id), qualified, done)) return false;
}
args.append(')');
s = args.toString();
get_base_right = true;
break;
}
}
s = "<Function>";
break;
}
}
if (s != null) {
if ((flags & ISymbols.SYM_FLAG_VOLATILE_TYPE) != 0) s = "volatile " + s;
if ((flags & ISymbols.SYM_FLAG_CONST_TYPE) != 0) s = "const " + s;
}
}
if (s == null) {
name = "N/A";
break;
}
if (name == null) {
name = s;
}
else if (get_base_left) {
name = s + name;
}
else if (get_base_right) {
if (name_left) name = "(" + name + ")" + s;
else name = name + s;
}
else {
name = s + " " + name;
}
if (!get_base_left && !get_base_right) break;
name_left = get_base_left;
if (name.length() > 0x1000) {
/* Must be invalid symbols data */
name = "<Unknown>";
break;
}
type_cache = model.getSymbolInfoCache(type_symbol.getBaseTypeID());
if (type_cache == null) {
name = "<Unknown> " + name;
break;
}
}
bf.append(name);
return true;
}
private String getQualifiedTypeNamePrefix(ISymbols.Symbol type_symbol, Runnable done) {
String prefix = "";
String containerId = type_symbol.getContainerID();
while (containerId != null) {
TCFDataCache<ISymbols.Symbol> ns_cache = model.getSymbolInfoCache(containerId);
if (!ns_cache.validate(done)) return null;
containerId = null;
ISymbols.Symbol ns_data = ns_cache.getData();
if (ns_data != null && (ns_data.getSymbolClass() == ISymbols.SymbolClass.namespace
|| ns_data.getSymbolClass() == ISymbols.SymbolClass.type)) {
String ns_name = ns_data.getName();
if (ns_name != null) {
prefix = ns_name + "::" + prefix;
containerId = ns_data.getContainerID();
}
}
}
return prefix;
}
private String toASCIIString(byte[] data, int offs, int size, char quote_char) {
StringBuffer bf = new StringBuffer();
bf.append(quote_char);
for (int i = 0; i < size; i++) {
int ch = data[offs + i] & 0xff;
if (ch >= ' ' && ch < 0x7f) {
bf.append((char)ch);
}
else {
switch (ch) {
case '\r': bf.append("\\r"); break;
case '\n': bf.append("\\n"); break;
case '\b': bf.append("\\b"); break;
case '\t': bf.append("\\t"); break;
case '\f': bf.append("\\f"); break;
default:
bf.append('\\');
bf.append((char)('0' + ch / 64));
bf.append((char)('0' + ch / 8 % 8));
bf.append((char)('0' + ch % 8));
}
}
}
if (data.length <= offs + size || data[offs + size] == 0) bf.append(quote_char);
else bf.append("...");
return bf.toString();
}
@SuppressWarnings("incomplete-switch")
private String toNumberString(int radix, ISymbols.TypeClass t, byte[] data, int offs, int size,
boolean big_endian, Number bin_scale, Number dec_scale) {
if (size <= 0) return "";
if (radix != 16) {
switch (t) {
case array:
case composite:
return "";
}
}
if (data == null) return "N/A";
if (radix == 2) {
StringBuffer bf = new StringBuffer();
int i = size * 8;
while (i > 0) {
if (i % 4 == 0 && bf.length() > 0) bf.append(',');
i--;
int j = i / 8;
if (big_endian) j = size - j - 1;
if ((data[offs + j] & (1 << (i % 8))) != 0) {
bf.append('1');
}
else {
bf.append('0');
}
}
return bf.toString();
}
if (radix == 10) {
switch (t) {
case integer:
case cardinal:
boolean sign_extend = t == ISymbols.TypeClass.integer;
if (bin_scale != null) {
int s = bin_scale.intValue();
BigInteger n = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, sign_extend);
if (s < 0) {
BigDecimal d = new BigDecimal(n);
while (s < 0) {
d = d.divide(BigDecimal.valueOf(2));
s++;
}
return d.toString();
}
return n.shiftLeft(s).toString() + '.';
}
if (dec_scale != null) {
BigDecimal d = new BigDecimal(TCFNumberFormat.toBigInteger(
data, offs, size, big_endian, sign_extend));
d = d.scaleByPowerOfTen(dec_scale.intValue());
return d.toString();
}
return TCFNumberFormat.toBigInteger(data, offs, size, big_endian, sign_extend).toString();
case real:
if (size > 16) return "";
return TCFNumberFormat.toFPString(data, offs, size, big_endian);
case complex:
if (size > 32) return "";
return TCFNumberFormat.toComplexFPString(data, offs, size, big_endian);
}
}
String s = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, false).toString(radix);
switch (radix) {
case 8:
if (!s.startsWith("0")) s = "0" + s;
break;
case 16:
if (s.length() < size * 2) {
StringBuffer bf = new StringBuffer();
while (bf.length() + s.length() < size * 2) bf.append('0');
bf.append(s);
s = bf.toString();
}
break;
}
assert s != null;
return s;
}
private String toNumberString(int radix) {
String s = null;
IExpressions.Value val = value.getData();
if (val != null) {
byte[] data = val.getValue();
if (data != null) {
ISymbols.TypeClass t = val.getTypeClass();
if (t == ISymbols.TypeClass.unknown && type.getData() != null) t = type.getData().getTypeClass();
Number bin_scale = (Number)val.getProperties().get(IExpressions.VAL_BINARY_SCALE);
Number dec_scale = (Number)val.getProperties().get(IExpressions.VAL_DECIMAL_SCALE);
s = toNumberString(radix, t, data, 0, data.length, val.isBigEndian(), bin_scale, dec_scale);
}
}
if (s == null) s = "N/A";
return s;
}
private void setLabel(ILabelUpdate result, String name, int col, int radix) {
String s = toNumberString(radix);
if (name == null) {
result.setLabel(s, col);
}
else {
result.setLabel(name + " = " + s, col);
}
}
private boolean isValueChanged(IExpressions.Value x, IExpressions.Value y) {
if (x == null || y == null) return false;
byte[] xb = x.getValue();
byte[] yb = y.getValue();
if (xb == null || yb == null) return false;
if (xb.length != yb.length) return true;
for (int i = 0; i < xb.length; i++) {
if (xb[i] != yb[i]) return true;
}
return false;
}
private boolean isShowTypeNamesEnabled(IPresentationContext context) {
Boolean attribute = (Boolean)context.getProperty(IDebugModelPresentation.DISPLAY_VARIABLE_TYPE_NAMES);
return attribute != null && attribute;
}
private BigInteger getLowerBound(Runnable done) {
TCFNode n = parent;
while (n instanceof TCFNodeArrayPartition) n = n.parent;
TCFDataCache<ISymbols.Symbol> t = ((TCFNodeExpression)n).getType();
if (!t.validate(done)) return null;
ISymbols.Symbol s = t.getData();
if (s != null) {
Number l = s.getLowerBound();
if (l != null) return JSON.toBigInteger(l);
}
return BigInteger.valueOf(0);
}
@Override
protected boolean getData(ILabelUpdate result, Runnable done) {
if (is_empty) {
result.setLabel("Add new expression", 0);
result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_NEW_EXPRESSION), 0);
}
else if (enabled || script == null) {
TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id);
TCFDataCache<?> pending = null;
if (field != null && !field.validate()) pending = field;
if (reg_id != null && !expression_text.validate(done)) pending = expression_text;
if (!var_expression.validate()) pending = var_expression;
if (!base_text.validate()) pending = base_text;
if (!value.validate()) pending = value;
if (!type.validate()) pending = type;
if (pending != null) {
if (script != null) {
if (!rem_expression.validate(done)) return false;
IExpressions.Expression exp_data = rem_expression.getData();
if (exp_data != null && exp_data.hasFuncCall()) {
/* Don't wait, it can take very long time */
pending.wait(post_delta);
result.setForeground(ColorCache.rgb_disabled, 0);
result.setLabel(script + " (Running)", 0);
return true;
}
}
pending.wait(done);
return false;
}
String name = null;
if (field != null) {
ISymbols.Symbol field_data = field.getData();
name = field_data.getName();
if (name == null && field_data.getFlag(ISymbols.SYM_FLAG_INHERITANCE)) {
TCFDataCache<ISymbols.Symbol> type = model.getSymbolInfoCache(field_data.getTypeID());
if (type != null) {
if (!type.validate(done)) return false;
ISymbols.Symbol type_data = type.getData();
if (type_data != null) name = type_data.getName();
}
}
}
else if (index >= 0) {
BigInteger lower_bound = getLowerBound(done);
if (lower_bound == null) return false;
name = "[" + lower_bound.add(JSON.toBigInteger(index)) + "]";
}
else if (deref) {
name = "*";
}
if (name == null && reg_id != null && expression_text.getData() != null) {
name = expression_text.getData();
}
if (name == null && var_expression.getData() != null) {
TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(var_expression.getData().getSymbolID());
if (var != null) {
if (!var.validate(done)) return false;
ISymbols.Symbol var_data = var.getData();
if (var_data != null) {
name = var_data.getName();
if (name == null && var_data.getFlag(ISymbols.SYM_FLAG_VARARG)) name = "<VarArg>";
if (name == null) name = "<" + var_data.getID() + ">";
}
}
}
if (name == null && base_text.getData() != null) {
name = base_text.getData();
}
if (name != null) {
String cast = model.getCastToType(id);
if (cast != null) name = "(" + cast + ")(" + name + ")";
}
Throwable error = base_text.getError();
if (error == null) error = value.getError();
String[] cols = result.getColumnIds();
if (error != null) {
if (cols == null || cols.length <= 1) {
result.setForeground(ColorCache.rgb_error, 0);
if (isShowTypeNamesEnabled(result.getPresentationContext())) {
if (!type_name.validate(done)) return false;
result.setLabel(name + ": N/A" + " , Type = " + type_name.getData(), 0);
}
else {
result.setLabel(name + ": N/A", 0);
}
}
else {
for (int i = 0; i < cols.length; i++) {
String c = cols[i];
if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
result.setLabel(name, i);
}
else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) {
if (!type_name.validate(done)) return false;
result.setLabel(type_name.getData(), i);
}
else {
result.setForeground(ColorCache.rgb_error, i);
result.setLabel("N/A", i);
}
}
}
}
else {
if (cols == null) {
StyledStringBuffer s = getPrettyExpression(done);
if (s == null) return false;
if (isShowTypeNamesEnabled(result.getPresentationContext())) {
if (!type_name.validate(done)) return false;
result.setLabel(name + " = " + s + " , Type = " + type_name.getData(), 0);
}
else {
result.setLabel(name + " = " + s, 0);
}
}
else {
for (int i = 0; i < cols.length; i++) {
String c = cols[i];
if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
result.setLabel(name, i);
}
else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) {
if (!type_name.validate(done)) return false;
result.setLabel(type_name.getData(), i);
}
else if (c.equals(TCFColumnPresentationExpression.COL_HEX_VALUE)) {
setLabel(result, null, i, 16);
}
else if (c.equals(TCFColumnPresentationExpression.COL_DEC_VALUE)) {
setLabel(result, null, i, 10);
}
else if (c.equals(TCFColumnPresentationExpression.COL_VALUE)) {
StyledStringBuffer s = getPrettyExpression(done);
if (s == null) return false;
result.setLabel(s.toString(), i);
}
}
}
}
next_value = value.getData();
if (isValueChanged(prev_value, next_value)) {
if (cols != null) {
for (int i = 1; i < cols.length; i++) {
result.setBackground(ColorCache.rgb_highlight, i);
}
}
else {
result.setForeground(ColorCache.rgb_no_columns_color_change, 0);
}
}
ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown;
ISymbols.Symbol type_symbol = type.getData();
if (type_symbol != null) {
type_class = type_symbol.getTypeClass();
}
switch (type_class) {
case pointer:
result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_POINTER), 0);
break;
case composite:
case array:
result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_AGGREGATE), 0);
break;
default:
result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE), 0);
}
}
else {
String[] cols = result.getColumnIds();
if (cols == null || cols.length <= 1) {
result.setForeground(ColorCache.rgb_disabled, 0);
result.setLabel(script, 0);
}
else {
for (int i = 0; i < cols.length; i++) {
String c = cols[i];
if (c.equals(TCFColumnPresentationExpression.COL_NAME)) {
result.setForeground(ColorCache.rgb_disabled, i);
result.setLabel(script, i);
}
}
}
}
return true;
}
@Override
protected void getFontData(ILabelUpdate update, String view_id) {
if (is_empty) {
update.setFontData(TCFModelFonts.getItalicFontData(view_id), 0);
}
else {
FontData fn = TCFModelFonts.getNormalFontData(view_id);
String[] cols = update.getColumnIds();
if (cols == null || cols.length == 0) {
update.setFontData(fn, 0);
}
else {
String[] ids = update.getColumnIds();
for (int i = 0; i < cols.length; i++) {
if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(ids[i]) ||
TCFColumnPresentationExpression.COL_DEC_VALUE.equals(ids[i]) ||
TCFColumnPresentationExpression.COL_VALUE.equals(ids[i])) {
update.setFontData(TCFModelFonts.getMonospacedFontData(view_id), i);
}
else {
update.setFontData(fn, i);
}
}
}
}
}
private StyledStringBuffer getPrettyExpression(Runnable done) {
for (ITCFPrettyExpressionProvider p : TCFPrettyExpressionProvider.getProviders()) {
TCFDataCache<String> c = p.getText(this);
if (c != null) {
if (!c.validate(done)) return null;
if (c.getError() == null && c.getData() != null) {
StyledStringBuffer bf = new StyledStringBuffer();
bf.append(c.getData(), StyledStringBuffer.MONOSPACED);
return bf;
}
}
}
if (!value.validate(done)) return null;
if (!string.validate(done)) return null;
StyledStringBuffer bf = new StyledStringBuffer();
if (string.getData() != null) {
bf.append(string.getData());
}
else {
IExpressions.Value v = value.getData();
if (v != null) {
byte[] data = v.getValue();
if (data != null) {
if (!appendValueText(bf, 1, v.getTypeID(), null, this,
data, 0, data.length, v.isBigEndian(), done)) return null;
}
}
}
return bf;
}
private boolean appendArrayValueText(StyledStringBuffer bf, int level,
ISymbols.Symbol type, Set<String> enclosing_structs,
byte[] data, int offs, int size, boolean big_endian, Number bit_stride, Runnable done) {
assert offs + size <= data.length;
int length = type.getLength();
bf.append('[');
if (length > 0) {
for (int n = 0; n < length; n++) {
if (n >= 100) {
bf.append("...");
break;
}
if (n > 0) bf.append(", ");
if (bit_stride != null) {
int bits = bit_stride.intValue();
String base_type_id = type.getBaseTypeID();
ISymbols.Symbol base_type_data = null;
if (base_type_id != null) {
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(base_type_id);
if (!type_cache.validate(done)) return false;
base_type_data = type_cache.getData();
}
int base_type_size = 0;
if (base_type_data != null) base_type_size = base_type_data.getSize();
if (base_type_size * 8 < bits) base_type_size = (bits + 7) / 8;
byte[] buf = new byte[base_type_size];
if (big_endian) {
for (int i = 0; i < bits; i++) {
int j = n * bits + i;
int k = j / 8;
int l = base_type_size * 8 - bits + i;
if (k < size && offs + k < data.length && (data[offs + k] & (1 << (7 - j % 8))) != 0) {
buf[l / 8] |= 1 << (7 - l % 8);
}
}
if (base_type_data != null && base_type_data.getTypeClass() == ISymbols.TypeClass.integer) {
/* Sign extension */
int sign_offs = base_type_size * 8 - bits;
boolean sign = (buf[sign_offs / 8] & (1 << (7 - sign_offs % 8))) != 0;
if (sign) {
for (int i = 0; i < sign_offs; i++) buf[i / 8] |= 1 << (7 - i % 8);
}
}
}
else {
for (int i = 0; i < bits; i++) {
int j = n * bits + i;
int k = j / 8;
if (k < size && offs + k < data.length && (data[offs + k] & (1 << (j % 8))) != 0) {
buf[i / 8] |= 1 << (i % 8);
}
}
if (base_type_data != null && base_type_data.getTypeClass() == ISymbols.TypeClass.integer) {
/* Sign extension */
int sign_offs = bits - 1;
boolean sign = (buf[sign_offs / 8] & (1 << (sign_offs % 8))) != 0;
if (sign) {
for (int i = bits; i < base_type_size * 8; i++) buf[i / 8] |= 1 << (i % 8);
}
}
}
if (!appendValueText(bf, level + 1, base_type_id, enclosing_structs, null,
buf, 0, buf.length, big_endian, done)) return false;
}
else {
int elem_size = size / length;
if (!appendValueText(bf, level + 1, type.getBaseTypeID(), enclosing_structs, null,
data, offs + n * elem_size, elem_size, big_endian, done)) return false;
}
}
}
bf.append(']');
return true;
}
private boolean appendCompositeValueText(
StyledStringBuffer bf, int level,
ISymbols.Symbol type, Set<String> enclosing_structs,
TCFNodeExpression data_node, boolean data_deref,
byte[] data, int offs, int size, boolean big_endian, Runnable done) {
TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type.getID());
if (children_cache == null) {
bf.append("...");
return true;
}
if (!children_cache.validate(done)) return false;
String[] children_data = children_cache.getData();
if (children_data == null) {
bf.append("...");
return true;
}
Set<String> structs = new HashSet<String>();
if (enclosing_structs != null) structs.addAll(enclosing_structs);
structs.add(type.getID());
int cnt = 0;
TCFDataCache<?> pending = null;
for (String id : children_data) {
TCFDataCache<ISymbols.Symbol> field_cache = model.getSymbolInfoCache(id);
if (!field_cache.validate()) {
pending = field_cache;
continue;
}
ISymbols.Symbol field_props = field_cache.getData();
if (field_props == null) continue;
if (field_props.getSymbolClass() != ISymbols.SymbolClass.reference) continue;
if (field_props.getFlag(ISymbols.SYM_FLAG_ARTIFICIAL)) continue;
String name = field_props.getName();
if (name == null && field_props.getFlag(ISymbols.SYM_FLAG_INHERITANCE)) {
name = type.getName();
}
if (structs.contains(field_props.getTypeID())) {
/*
* Avoid infinite loop caused by declaration like: class X { static X x; ... }
* See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=506266
*/
if (cnt > 0) bf.append(", ");
bf.append(name);
bf.append('=');
bf.append("...");
cnt++;
continue;
}
TCFNodeExpression field_node = null;
if (data_node != null) {
if (!data_node.children.validate(done)) return false;
field_node = data_node.children.getField(id, data_deref);
}
if (field_props.getProperties().get(ISymbols.PROP_OFFSET) == null) {
// Bitfield - use field_node to retrieve the value
if (name == null || field_node == null) continue;
if (cnt > 0) bf.append(", ");
bf.append(name);
bf.append('=');
if (!field_node.value.validate(done)) return false;
IExpressions.Value field_value = field_node.value.getData();
byte[] field_data = field_value != null ? field_value.getValue() : null;
if (field_data == null) {
bf.append('?');
}
else {
if (!field_node.appendValueText(bf, level + 1, field_props.getTypeID(), structs, field_node,
field_data, 0, field_data.length, field_value.isBigEndian(), done)) return false;
}
cnt++;
continue;
}
int f_offs = field_props.getOffset();
int f_size = field_props.getSize();
if (cnt > 0) bf.append(", ");
if (name != null) {
bf.append(name);
bf.append('=');
}
if (offs + f_offs + f_size > data.length) {
bf.append('?');
}
else {
boolean big_endian_field = big_endian;
if ((field_props.getFlags() & ISymbols.SYM_FLAG_BIG_ENDIAN) != 0) big_endian_field = true;
if ((field_props.getFlags() & ISymbols.SYM_FLAG_LITTLE_ENDIAN) != 0) big_endian_field = false;
if (!appendValueText(bf, level + 1, field_props.getTypeID(), structs, field_node,
data, offs + f_offs, f_size, big_endian_field, done)) return false;
}
cnt++;
}
if (pending == null) return true;
pending.wait(done);
return false;
}
private boolean appendNumericValueText( StyledStringBuffer bf, ISymbols.TypeClass type_class, Runnable done) {
if (!type.validate(done)) return false;
IExpressions.Value v = value.getData();
Number bin_scale = (Number)v.getProperties().get(IExpressions.VAL_BINARY_SCALE);
Number dec_scale = (Number)v.getProperties().get(IExpressions.VAL_DECIMAL_SCALE);
boolean big_endian = v.isBigEndian();
byte[] data = v.getValue();
bf.append("Hex: ", SWT.BOLD);
bf.append(toNumberString(16, type_class, data, 0, data.length, big_endian, bin_scale, dec_scale), StyledStringBuffer.MONOSPACED);
bf.append(", ");
bf.append("Dec: ", SWT.BOLD);
bf.append(toNumberString(10, type_class, data, 0, data.length, big_endian, bin_scale, dec_scale), StyledStringBuffer.MONOSPACED);
bf.append(", ");
bf.append("Oct: ", SWT.BOLD);
bf.append(toNumberString(8, type_class, data, 0, data.length, big_endian, bin_scale, dec_scale), StyledStringBuffer.MONOSPACED);
if (v.getTypeClass() == ISymbols.TypeClass.pointer) {
TCFNode p = parent;
while (p != null) {
if (p instanceof TCFNodeExecContext) {
TCFNodeExecContext exe = (TCFNodeExecContext)p;
BigInteger addr = TCFNumberFormat.toBigInteger(data, 0, data.length, big_endian, false);
if (!exe.appendPointedObject(bf, addr, done)) return false;
break;
}
p = p.parent;
}
}
bf.append('\n');
bf.append("Bin: ", SWT.BOLD);
bf.append(toNumberString(2), StyledStringBuffer.MONOSPACED);
bf.append('\n');
return true;
}
@SuppressWarnings("incomplete-switch")
private boolean appendValueText(
StyledStringBuffer bf, int level, String type_id, Set<String> enclosing_structs,
TCFNodeExpression data_node, byte[] data, int offs, int size, boolean big_endian, Runnable done) {
if (data == null) return true;
ISymbols.Symbol type_data = null;
if (type_id != null) {
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id);
if (!type_cache.validate(done)) return false;
type_data = type_cache.getData();
}
if (type_data == null) {
ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown;
if (!value.validate(done)) return false;
if (value.getData() != null) type_class = value.getData().getTypeClass();
if (level == 0) {
assert offs == 0 && size == data.length && data_node == this;
if (size > 0 && !appendNumericValueText(bf, type_class, done)) return false;
}
else if (size == 0) {
bf.append("N/A", StyledStringBuffer.MONOSPACED);
}
else if (type_class == ISymbols.TypeClass.integer ||
type_class == ISymbols.TypeClass.real ||
type_class == ISymbols.TypeClass.complex) {
bf.append(toNumberString(10, type_class, data, offs, size, big_endian, null, null), StyledStringBuffer.MONOSPACED);
}
else {
bf.append("0x", StyledStringBuffer.MONOSPACED);
bf.append(toNumberString(16, type_class, data, offs, size, big_endian, null, null), StyledStringBuffer.MONOSPACED);
}
return true;
}
if (level == 0) {
StyledStringBuffer s = getPrettyExpression(done);
if (s == null) return false;
if (s.length() > 0) {
bf.append(s);
bf.append('\n');
}
else if (string.getError() != null) {
bf.append("Cannot read pointed value: ", SWT.BOLD, null, ColorCache.rgb_error);
bf.append(TCFModel.getErrorMessage(string.getError(), false), SWT.ITALIC, null, ColorCache.rgb_error);
bf.append('\n');
}
}
if (type_data.getSize() > 0) {
if (level >= 32) {
/* Stop potentially infinite recursion */
bf.append("...");
return true;
}
ISymbols.TypeClass type_class = type_data.getTypeClass();
Number bin_scale = null;
Number dec_scale = null;
Number bit_stride = null;
ISymbols.Symbol base_type = type_data;
for (int i = 0; i < max_type_chain_length; i++) {
if (base_type == null) break;
if (type_class == ISymbols.TypeClass.array) {
if ((bit_stride = (Number)base_type.getProperties().get(ISymbols.PROP_BIT_STRIDE)) != null) break;
}
else {
if ((bin_scale = (Number)base_type.getProperties().get(ISymbols.PROP_BINARY_SCALE)) != null) break;
if ((dec_scale = (Number)base_type.getProperties().get(ISymbols.PROP_DECIMAL_SCALE)) != null) break;
}
String id = base_type.getTypeID();
if (id == null || id.equals(base_type.getID())) break;
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(id);
if (!type_cache.validate(done)) return false;
base_type = type_cache.getData();
}
switch (type_class) {
case enumeration:
case integer:
case cardinal:
case real:
case complex:
if (level == 0) {
assert offs == 0 && size == data.length && data_node == this;
if (!appendNumericValueText(bf, type_class, done)) return false;
}
else if (type_data.getTypeClass() == ISymbols.TypeClass.cardinal) {
bf.append("0x", StyledStringBuffer.MONOSPACED);
bf.append(toNumberString(16, type_class, data, offs, size, big_endian, bin_scale, dec_scale), StyledStringBuffer.MONOSPACED);
}
else {
bf.append(toNumberString(10, type_class, data, offs, size, big_endian, bin_scale, dec_scale), StyledStringBuffer.MONOSPACED);
}
break;
case pointer:
case function:
case member_pointer:
if (level == 0) {
assert offs == 0 && size == data.length && data_node == this;
if (!appendNumericValueText(bf, type_class, done)) return false;
}
else {
bf.append("0x", StyledStringBuffer.MONOSPACED);
bf.append(toNumberString(16, type_class, data, offs, size, big_endian, null, null), StyledStringBuffer.MONOSPACED);
}
break;
case array:
if (level > 0) {
if (!appendArrayValueText(bf, level, type_data, enclosing_structs,
data, offs, size, big_endian, bit_stride, done)) return false;
}
break;
case composite:
if (level > 0) {
bf.append('{');
if (!appendCompositeValueText(bf, level, type_data, enclosing_structs, data_node, false,
data, offs, size, big_endian, done)) return false;
bf.append('}');
}
break;
}
}
return true;
}
private String getRegisterName(String reg_id, Runnable done) {
String name = reg_id;
if (!model.createNode(reg_id, done)) return null;
TCFNodeRegister reg_node = (TCFNodeRegister)model.getNode(reg_id);
if (reg_node != null) {
TCFDataCache<IRegisters.RegistersContext> reg_ctx_cache = reg_node.getContext();
if (!reg_ctx_cache.validate(done)) return null;
IRegisters.RegistersContext reg_ctx_data = reg_ctx_cache.getData();
if (reg_ctx_data != null && reg_ctx_data.getName() != null) name = reg_ctx_data.getName();
}
return name;
}
public boolean getDetailText(StyledStringBuffer bf, Runnable done) {
if (is_empty) return true;
if (!enabled) {
bf.append("Disabled");
return true;
}
if (!rem_expression.validate(done)) return false;
if (rem_expression.getError() == null) {
if (!value.validate(done)) return false;
if (!type_name.validate(done)) return false;
IExpressions.Value v = value.getData();
if (v != null) {
String type_id = v.getTypeID();
if (v.isImplicitPointer()) {
bf.append("Implicit pointer, value not available\n", SWT.BOLD);
}
else if (value.getError() == null) {
byte[] data = v.getValue();
if (data != null) {
boolean big_endian = v.isBigEndian();
if (!appendValueText(bf, 0, type_id, null, this,
data, 0, data.length, big_endian, done)) return false;
}
}
ISymbols.Symbol type_data = null;
if (type_id != null) {
TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id);
if (!type_cache.validate(done)) return false;
type_data = type_cache.getData();
}
Map<String,Object> value_props = v.getProperties();
Number bin_scale = (Number)value_props.get(IExpressions.VAL_BINARY_SCALE);
Number dec_scale = (Number)value_props.get(IExpressions.VAL_DECIMAL_SCALE);
Number bit_stride = (Number)value_props.get(IExpressions.VAL_BIT_STRIDE);
boolean fst = true;
if (bin_scale != null) {
bf.append("Binary Scale: ", SWT.BOLD);
bf.append(bin_scale.toString(), StyledStringBuffer.MONOSPACED);
fst = false;
}
if (dec_scale != null) {
if (!fst) bf.append(", ");
bf.append("Decimal Scale: ", SWT.BOLD);
bf.append(dec_scale.toString(), StyledStringBuffer.MONOSPACED);
fst = false;
}
if (bit_stride != null) {
if (!fst) bf.append(", ");
bf.append("Stride: ", SWT.BOLD);
bf.append(bit_stride.toString(), StyledStringBuffer.MONOSPACED);
bf.append(bit_stride.longValue() == 1 ? " bit" : " bits");
fst = false;
}
if (v.getValue() != null) {
int sz = v.getValue().length;
if (!fst) bf.append(", ");
bf.append("Size: ", SWT.BOLD);
bf.append(Integer.toString(sz), StyledStringBuffer.MONOSPACED);
bf.append(sz == 1 ? " byte" : " bytes");
fst = false;
}
else if (type_data != null) {
int sz = type_data.getSize();
if (!fst) bf.append(", ");
bf.append("Size: ", SWT.BOLD);
bf.append(Integer.toString(sz), StyledStringBuffer.MONOSPACED);
bf.append(sz == 1 ? " byte" : " bytes");
fst = false;
}
else {
IExpressions.Expression exp = rem_expression.getData();
if (exp != null) {
int sz = exp.getSize();
if (!fst) bf.append(", ");
bf.append("Size: ", SWT.BOLD);
bf.append(Integer.toString(sz), StyledStringBuffer.MONOSPACED);
bf.append(sz == 1 ? " byte" : " bytes");
fst = false;
}
}
String tnm = type_name.getData();
if (tnm != null) {
if (!fst) bf.append(", ");
bf.append("Type: ", SWT.BOLD);
bf.append(tnm);
fst = false;
}
if (!fst) bf.append('\n');
@SuppressWarnings("unchecked")
List<Map<String,Object>> pieces = (List<Map<String,Object>>)value_props.get(IExpressions.VAL_PIECES);
if (pieces != null) {
bf.append("Pieces: ", SWT.BOLD);
int piece_cnt = 0;
for (Map<String,Object> props : pieces) {
int cnt = 0;
if (piece_cnt > 0) bf.append("; ");
String reg_id = (String)props.get("Register");
if (reg_id != null) {
String nm = getRegisterName(reg_id, done);
if (nm == null) return false;
bf.append("Register: ", SWT.BOLD);
bf.append(nm);
cnt++;
}
byte[] data = (byte[])props.get("Value");
if (data != null) {
bf.append("<Bin Value>", SWT.BOLD);
}
Number addr = (Number)props.get("Address");
if (addr != null) {
BigInteger i = JSON.toBigInteger(addr);
if (cnt > 0) bf.append(", ");
bf.append("Address: ", SWT.BOLD);
bf.append("0x", StyledStringBuffer.MONOSPACED);
bf.append(i.toString(16), StyledStringBuffer.MONOSPACED);
cnt++;
}
Number bit_offs = (Number)props.get("BitOffs");
if (bit_offs == null && props.get("BitSize") != null) bit_offs = BigInteger.ZERO;
if (bit_offs != null) {
BigInteger i = JSON.toBigInteger(bit_offs);
if (cnt > 0) bf.append(", ");
bf.append("Bit Offset: ", SWT.BOLD);
bf.append(i.toString(10), StyledStringBuffer.MONOSPACED);
cnt++;
}
Number bit_size = (Number)props.get("BitSize");
if (bit_size != null) {
BigInteger i = JSON.toBigInteger(bit_size);
if (cnt > 0) bf.append(", ");
bf.append("Bit Count: ", SWT.BOLD);
bf.append(i.toString(10), StyledStringBuffer.MONOSPACED);
cnt++;
}
Number byte_size = (Number)props.get("Size");
if (byte_size != null) {
BigInteger i = JSON.toBigInteger(byte_size);
if (cnt > 0) bf.append(", ");
bf.append("Byte Count: ", SWT.BOLD);
bf.append(i.toString(10), StyledStringBuffer.MONOSPACED);
cnt++;
}
piece_cnt++;
}
bf.append('\n');
}
else {
int cnt = 0;
String reg_id = v.getRegisterID();
if (reg_id != null) {
String nm = getRegisterName(reg_id, done);
if (nm == null) return false;
bf.append("Register: ", SWT.BOLD);
bf.append(nm);
cnt++;
}
TCFDataCache<ISymbols.Symbol> field_cache = model.getSymbolInfoCache(field_id);
if (field_cache != null) {
if (!field_cache.validate(done)) return false;
ISymbols.Symbol field_props = field_cache.getData();
if (field_props != null && field_props.getProperties().get(ISymbols.PROP_OFFSET) != null) {
if (cnt > 0) bf.append(", ");
bf.append("Offset: ", SWT.BOLD);
bf.append(Integer.toString(field_props.getOffset()), StyledStringBuffer.MONOSPACED);
cnt++;
}
}
Number addr = v.getAddress();
if (addr != null) {
BigInteger i = JSON.toBigInteger(addr);
if (cnt > 0) bf.append(", ");
bf.append("Address: ", SWT.BOLD);
bf.append("0x", StyledStringBuffer.MONOSPACED);
bf.append(i.toString(16), StyledStringBuffer.MONOSPACED);
cnt++;
}
if (cnt > 0) bf.append('\n');
}
}
if (value.getError() != null) {
bf.append(value.getError(), ColorCache.rgb_error);
}
}
else {
bf.append(rem_expression.getError(), ColorCache.rgb_error);
}
return true;
}
public String getValueText(boolean add_error_text, Runnable done) {
if (!rem_expression.validate(done)) return null;
if (!value.validate(done)) return null;
StyledStringBuffer bf = new StyledStringBuffer();
IExpressions.Value v = value.getData();
if (v != null) {
byte[] data = v.getValue();
if (data != null) {
boolean big_endian = v.isBigEndian();
if (!appendValueText(bf, 1, v.getTypeID(), null, this,
data, 0, data.length, big_endian, done)) return null;
}
}
if (add_error_text) {
if (bf.length() == 0 && rem_expression.getError() != null) {
bf.append(TCFModel.getErrorMessage(rem_expression.getError(), false));
}
if (bf.length() == 0 && value.getError() != null) {
bf.append(TCFModel.getErrorMessage(value.getError(), false));
}
}
return bf.toString();
}
@Override
protected boolean getData(IChildrenCountUpdate result, Runnable done) {
if (!is_empty && enabled) {
if (!children.validate(done)) return false;
result.setChildCount(children.size());
}
else {
result.setChildCount(0);
}
return true;
}
@Override
protected boolean getData(IChildrenUpdate result, Runnable done) {
if (is_empty || !enabled) return true;
return children.getData(result, done);
}
@Override
protected boolean getData(IHasChildrenUpdate result, Runnable done) {
if (!is_empty && enabled) {
if (!children.validate(done)) return false;
result.setHasChilren(children.size() > 0);
}
else {
result.setHasChilren(false);
}
return true;
}
@Override
public int compareTo(TCFNode n) {
TCFNodeExpression e = (TCFNodeExpression)n;
if (sort_pos < e.sort_pos) return -1;
if (sort_pos > e.sort_pos) return +1;
return 0;
}
public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) {
assert element == this;
if (TCFColumnPresentationExpression.COL_NAME.equals(column_id)) {
return new TextCellEditor(parent);
}
if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(column_id)) {
return new TextCellEditor(parent);
}
if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(column_id)) {
return new TextCellEditor(parent);
}
if (TCFColumnPresentationExpression.COL_VALUE.equals(column_id)) {
return new TextCellEditor(parent);
}
return null;
}
private static final ICellModifier cell_modifier = new ICellModifier() {
private Object original_value;
public boolean canModify(Object element, final String property) {
final TCFNodeExpression node = (TCFNodeExpression)element;
try {
return new TCFTask<Boolean>(node.channel) {
public void run() {
if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
done(node.is_empty || node.script != null);
return;
}
TCFNode n = node.getRootExpression().parent;
if (n instanceof TCFNodeStackFrame) {
TCFNodeExecContext exe = (TCFNodeExecContext)n.parent;
TCFDataCache<TCFContextState> state_cache = exe.getState();
if (!state_cache.validate(this)) return;
TCFContextState state_data = state_cache.getData();
if (state_data == null || !state_data.is_suspended) {
done(Boolean.FALSE);
return;
}
}
if (!node.is_empty && node.enabled) {
if (!node.rem_expression.validate(this)) return;
IExpressions.Expression exp = node.rem_expression.getData();
if (exp != null && exp.canAssign()) {
if (!node.value.validate(this)) return;
if (!node.type.validate(this)) return;
if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null);
return;
}
if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null);
return;
}
if (TCFColumnPresentationExpression.COL_VALUE.equals(property)) {
IExpressions.Value eval = node.value.getData();
StyledStringBuffer bf = node.getPrettyExpression(this);
if (bf == null) return;
String s = bf.toString();
done(s.startsWith("0x") ||
s.startsWith("'") && s.endsWith("'") ||
eval != null && eval.getTypeClass() == ISymbols.TypeClass.enumeration ||
TCFNumberFormat.isValidDecNumber(true, s) == null);
return;
}
}
}
done(Boolean.FALSE);
}
}.get(1, TimeUnit.SECONDS);
}
catch (Exception e) {
return false;
}
}
public Object getValue(Object element, final String property) {
original_value = null;
final TCFNodeExpression node = (TCFNodeExpression)element;
try {
return original_value = new TCFTask<String>() {
public void run() {
if (node.is_empty) {
done("");
return;
}
if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
done(node.script);
return;
}
if (!node.value.validate(this)) return;
if (node.value.getData() != null) {
if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
done(node.toNumberString(16));
return;
}
if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
done(node.toNumberString(10));
return;
}
if (TCFColumnPresentationExpression.COL_VALUE.equals(property)) {
StyledStringBuffer bf = node.getPrettyExpression(this);
if (bf == null) return;
done(bf.toString());
return;
}
}
done(null);
}
}.get(1, TimeUnit.SECONDS);
}
catch (Exception e) {
return null;
}
}
public void modify(Object element, final String property, final Object value) {
if (value == null) return;
if (original_value != null && original_value.equals(value)) return;
final TCFNodeExpression node = (TCFNodeExpression)element;
try {
new TCFTask<Boolean>() {
@SuppressWarnings("incomplete-switch")
public void run() {
try {
if (TCFColumnPresentationExpression.COL_NAME.equals(property)) {
if (value instanceof String) {
String s = ((String)value).trim();
IExpressionManager m = node.model.getExpressionManager();
if (node.is_empty) {
if (s.length() > 0) m.addExpression(m.newWatchExpression(s));
}
else if (node.platform_expression != null && !s.equals(node.script)) {
m.removeExpression(node.platform_expression);
if (s.length() > 0) m.addExpression(m.newWatchExpression(s));
}
}
done(Boolean.TRUE);
return;
}
if (!node.rem_expression.validate(this)) return;
IExpressions.Expression exp = node.rem_expression.getData();
if (exp != null && exp.canAssign()) {
byte[] bf = null;
int size = exp.getSize();
boolean is_enum = false;
boolean is_float = false;
boolean big_endian = false;
boolean signed = false;
if (!node.type.validate(this)) return;
if (!node.value.validate(this)) return;
IExpressions.Value eval = node.value.getData();
Number bin_scale = null;
Number dec_scale = null;
if (eval != null) {
switch(eval.getTypeClass()) {
case enumeration:
is_enum = true;
break;
case real:
is_float = true;
signed = true;
break;
case integer:
signed = true;
break;
}
bin_scale = (Number)eval.getProperties().get(IExpressions.VAL_BINARY_SCALE);
dec_scale = (Number)eval.getProperties().get(IExpressions.VAL_DECIMAL_SCALE);
big_endian = eval.isBigEndian();
size = eval.getValue().length;
}
String error = null;
String input = ((String)value).trim();
if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) {
if (input.startsWith("0x")) input = input.substring(2);
error = TCFNumberFormat.isValidHexNumber(input);
if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, signed, big_endian);
}
else if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) {
if (bin_scale != null) {
int n = bin_scale.intValue();
BigDecimal d = new BigDecimal(input);
while (n < 0) {
d = d.multiply(BigDecimal.valueOf(2));
n++;
}
bf = TCFNumberFormat.toByteArray(d.toBigInteger().toString(), 10, false, size, signed, big_endian);
}
else if (dec_scale != null) {
BigDecimal d = new BigDecimal(input).scaleByPowerOfTen(-dec_scale.intValue());
bf = TCFNumberFormat.toByteArray(d.toBigInteger().toString(), 10, false, size, signed, big_endian);
}
else {
error = TCFNumberFormat.isValidDecNumber(is_float, input);
if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, signed, big_endian);
}
}
else if (TCFColumnPresentationExpression.COL_VALUE.equals(property)) {
if (input.startsWith("0x")) {
String s = input.substring(2);
error = TCFNumberFormat.isValidHexNumber(s);
if (error == null) bf = TCFNumberFormat.toByteArray(s, 16, false, size, signed, big_endian);
}
else if (input.startsWith("'") && input.endsWith("'")) {
String s = input.substring(1, input.length() - 1);
int l = s.length();
int i = 0;
int n = 0;
if (i < l) {
char ch = s.charAt(i++);
if (ch == '\\' && i < l) {
ch = s.charAt(i++);
switch (ch) {
case 'r': n = '\r'; break;
case 'n': n = '\n'; break;
case 'b': n = '\b'; break;
case 't': n = '\t'; break;
case 'f': n = '\f'; break;
default:
while (ch >= '0' && ch <= '7') {
n = n * 8 + (ch - '0');
if (i >= l) break;
ch = s.charAt(i++);
}
}
}
else {
n = ch;
}
}
if (i < l) throw new Exception("Invalid character literal: " + value);
bf = TCFNumberFormat.toByteArray(Integer.toString(n), 10, false, size, signed, big_endian);
}
else if (bin_scale != null) {
int n = bin_scale.intValue();
BigDecimal d = new BigDecimal(input);
while (n < 0) {
d = d.multiply(BigDecimal.valueOf(2));
n++;
}
bf = TCFNumberFormat.toByteArray(d.toBigInteger().toString(), 10, false, size, signed, big_endian);
}
else if (dec_scale != null) {
BigDecimal d = new BigDecimal(input).scaleByPowerOfTen(-dec_scale.intValue());
bf = TCFNumberFormat.toByteArray(d.toBigInteger().toString(), 10, false, size, signed, big_endian);
}
else {
error = TCFNumberFormat.isValidDecNumber(is_float, input);
if (error == null) {
bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, signed, big_endian);
}
else if (is_enum) {
TCFModel model = node.model;
ISymbols.Symbol type_data = node.type.getData();
TCFDataCache<String[]> type_children_cache = model.getSymbolChildrenCache(type_data.getID());
if (!type_children_cache.validate(this)) return;
String[] type_children_data = type_children_cache.getData();
if (type_children_data != null) {
for (String const_id : type_children_data) {
TCFDataCache<ISymbols.Symbol> const_cache = model.getSymbolInfoCache(const_id);
if (!const_cache.validate(this)) return;
ISymbols.Symbol const_data = const_cache.getData();
if (const_data != null && input.equals(const_data.getName()) && const_data.getValue() != null) {
bf = const_data.getValue();
error = null;
break;
}
}
}
if (error != null && (input.length() == 0 || input.charAt(0) < '0' || input.charAt(0) > '9')) {
error = "Expected enumerator name or integer number";
}
}
}
}
if (error != null) throw new Exception("Invalid value: " + value, new Exception(error));
if (bf != null) {
IExpressions exps = node.launch.getService(IExpressions.class);
exps.assign(exp.getID(), bf, new IExpressions.DoneAssign() {
public void doneAssign(IToken token, Exception error) {
node.getRootExpression().onValueChanged();
if (error != null) {
node.model.showMessageBox("Cannot modify element value", error);
done(Boolean.FALSE);
}
else {
done(Boolean.TRUE);
}
}
});
return;
}
}
done(Boolean.FALSE);
}
catch (Throwable x) {
node.model.showMessageBox("Cannot modify element value", x);
done(Boolean.FALSE);
}
}
}.get(10, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
node.model.showMessageBox("Timeout modifying element value", new Exception("No response for 10 seconds."));
}
catch (Exception e) {
node.model.showMessageBox("Error modifying element value", e);
}
}
};
public ICellModifier getCellModifier(IPresentationContext context, Object element) {
assert element == this;
return cell_modifier;
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (platform_expression != null) {
if (adapter == IExpression.class) {
return platform_expression;
}
if (adapter == IWatchExpression.class) {
if (platform_expression instanceof IWatchExpression) {
return platform_expression;
}
}
}
return super.getAdapter(adapter);
}
}