blob: 3ec8ad4392751484f8455cd19b59fd4bf14b4e04 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.debugger.jpda.truffle.vars;
import java.io.InvalidObjectException;
import org.netbeans.api.debugger.jpda.Field;
import org.netbeans.api.debugger.jpda.InvalidExpressionException;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.ObjectVariable;
import org.netbeans.api.debugger.jpda.Variable;
import org.netbeans.modules.debugger.jpda.models.AbstractVariable;
import org.netbeans.modules.debugger.jpda.truffle.LanguageName;
import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
import org.openide.util.Exceptions;
public class TruffleVariableImpl implements TruffleVariable {
private static final String GUEST_OBJECT_TYPE = "org.netbeans.modules.debugger.jpda.backend.truffle.GuestObject"; // NOI18N
private static final String FIELD_NAME = "name"; // NOI18N
private static final String FIELD_LANGUAGE = "language"; // NOI18N
private static final String FIELD_TYPE = "type"; // NOI18N
private static final String FIELD_READABLE = "readable"; // NOI18N
private static final String FIELD_WRITABLE = "writable"; // NOI18N
private static final String FIELD_INTERNAL = "internal"; // NOI18N
private static final String FIELD_LEAF = "leaf"; // NOI18N
private static final String FIELD_DISPLAY_VALUE = "displayValue"; // NOI18N
private static final String METHOD_GET_CHILDREN = "getProperties"; // NOI18N
private static final String METHOD_GET_CHILDREN_SIG = "()[Lorg/netbeans/modules/debugger/jpda/backend/truffle/GuestObject;"; // NOI18N
private static final String METHOD_SET_VALUE = "setValue"; // NOI18N
private static final String METHOD_SET_VALUE_SIG = "(Lcom/oracle/truffle/api/debug/DebugStackFrame;Ljava/lang/String;)Lorg/netbeans/modules/debugger/jpda/backend/truffle/GuestObject;"; // NOI18N
private static final String FIELD_VALUE_SOURCE = "valueSourcePosition"; // NOI18N
private static final String FIELD_TYPE_SOURCE = "typeSourcePosition"; // NOI18N
private final ObjectVariable guestObject;
private final String name;
private final LanguageName language;
private final String type;
private final String displayValue;
private final boolean readable;
private final boolean writable;
private final boolean internal;
private final boolean leaf;
private final boolean hasValueSource;
private final boolean hasTypeSource;
private SourcePosition valueSource;
private SourcePosition typeSource;
private TruffleVariableImpl(ObjectVariable guestObject, String name,
LanguageName language, String type, String displayValue,
boolean readable, boolean writable, boolean internal,
boolean hasValueSource, boolean hasTypeSource,
boolean leaf) {
this.guestObject = guestObject;
this.name = name;
this.language = language;
this.type = type;
this.displayValue = displayValue;
this.readable = readable;
this.writable = writable;
this.internal = internal;
this.hasValueSource = hasValueSource;
this.hasTypeSource = hasTypeSource;
this.leaf = leaf;
}
public static TruffleVariableImpl get(Variable var) {
if (GUEST_OBJECT_TYPE.equals(var.getType())) {
ObjectVariable truffleObj = (ObjectVariable) var;
//System.err.println("TruffleVariableImpl.get("+var+") fields on "+truffleObj+", class "+truffleObj.getClassType().getName());
// The inner value can change in watches.
Field f = truffleObj.getField(FIELD_NAME);
if (f == null) {
return null;
}
String name = (String) f.createMirrorObject();
f = truffleObj.getField(FIELD_LANGUAGE);
if (f == null) {
return null;
}
LanguageName language = LanguageName.parse((String) f.createMirrorObject());
f = truffleObj.getField(FIELD_TYPE);
if (f == null) {
return null;
}
String type = (String) f.createMirrorObject();
f = truffleObj.getField(FIELD_DISPLAY_VALUE);
if (f == null) {
return null;
}
String dispVal = (String) f.createMirrorObject();
f = truffleObj.getField(FIELD_READABLE);
boolean readable;
if (f == null) {
readable = true;
} else {
readable = (Boolean) f.createMirrorObject();
}
f = truffleObj.getField(FIELD_WRITABLE);
boolean writable;
if (f == null) {
writable = true;
} else {
writable = (Boolean) f.createMirrorObject();
}
f = truffleObj.getField(FIELD_INTERNAL);
boolean internal;
if (f == null) {
internal = true;
} else {
internal = (Boolean) f.createMirrorObject();
}
f = truffleObj.getField(FIELD_VALUE_SOURCE);
boolean hasValueSource = ((ObjectVariable) f).getUniqueID() != 0L;
f = truffleObj.getField(FIELD_TYPE_SOURCE);
boolean hasTypeSource = ((ObjectVariable) f).getUniqueID() != 0L;
boolean leaf = isLeaf(truffleObj);
return new TruffleVariableImpl(truffleObj, name, language, type, dispVal, readable, writable, internal, hasValueSource, hasTypeSource, leaf);
} else {
return null;
}
}
static boolean isLeaf(ObjectVariable truffleObj) {
Field f = getFieldChecked(FIELD_LEAF, truffleObj);
Boolean mirrorLeaf = (Boolean) f.createMirrorObject();
boolean leaf;
if (mirrorLeaf == null) {
leaf = false;
} else {
leaf = mirrorLeaf;
}
return leaf;
}
@Override
public String getName() {
return name;
}
@Override
public LanguageName getLanguage() {
return language;
}
@Override
public String getType() {
return type;
}
@Override
public boolean isReadable() {
return readable;
}
@Override
public boolean isWritable() {
return writable;
}
@Override
public boolean isInternal() {
return internal;
}
public String getDisplayValue() {
return displayValue;
}
@Override
public Object getValue() {
return displayValue; // TODO
}
@Override
public ObjectVariable setValue(JPDADebugger debugger, String newExpression) {
if (this.displayValue.equals(newExpression)) {
return null;
}
return setValue(debugger, guestObject, newExpression);
}
static ObjectVariable setValue(JPDADebugger debugger, ObjectVariable guestObject, String newExpression) {
CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(debugger.getCurrentThread());
if (currentPCInfo != null) {
ObjectVariable selectedFrame = currentPCInfo.getSelectedStackFrame().getStackFrameInstance();
try {
Variable retVar = guestObject.invokeMethod(METHOD_SET_VALUE, METHOD_SET_VALUE_SIG,
new Variable[] { selectedFrame, debugger.createMirrorVar(newExpression) });
if (retVar instanceof ObjectVariable) {
return (ObjectVariable) retVar;
}
} catch (NoSuchMethodException | InvalidExpressionException | InvalidObjectException ex) {
Exceptions.printStackTrace(ex);
}
}
return null;
}
@Override
public boolean hasValueSource() {
return hasValueSource;
}
@Override
public synchronized SourcePosition getValueSource() {
if (valueSource == null) {
Field f = getFieldChecked(FIELD_VALUE_SOURCE, guestObject);
valueSource = TruffleAccess.getSourcePosition(((AbstractVariable) f).getDebugger(), (ObjectVariable) f);
}
return valueSource;
}
@Override
public boolean hasTypeSource() {
return hasTypeSource;
}
@Override
public synchronized SourcePosition getTypeSource() {
if (typeSource == null) {
Field f = getFieldChecked(FIELD_TYPE_SOURCE, guestObject);
typeSource = TruffleAccess.getSourcePosition(((AbstractVariable) f).getDebugger(), (ObjectVariable) f);
}
return typeSource;
}
@Override
public boolean isLeaf() {
return leaf;
}
@Override
public Object[] getChildren() {
return getChildren(guestObject);
}
static Object[] getChildren(ObjectVariable guestObject) {
try {
Variable children = guestObject.invokeMethod(METHOD_GET_CHILDREN, METHOD_GET_CHILDREN_SIG, new Variable[] {});
if (children instanceof ObjectVariable) {
Field[] fields = ((ObjectVariable) children).getFields(0, Integer.MAX_VALUE);
int n = fields.length;
Object[] ch = new Object[n];
for (int i = 0; i < n; i++) {
TruffleVariableImpl tv = get(fields[i]);
if (tv != null) {
ch[i] = tv;
} else {
ch[i] = fields[i].createMirrorObject();
}
}
return ch;
}
} catch (NoSuchMethodException | InvalidExpressionException ex) {
Exceptions.printStackTrace(ex);
}
return new Object[] {};
}
private static Field getFieldChecked(String fieldName, ObjectVariable guestObject) {
Field f = guestObject.getField(fieldName);
if (f == null) {
try {
throw new IllegalStateException("No "+fieldName+" field in "+guestObject.getToStringValue());
} catch (InvalidExpressionException iex) {
throw new IllegalStateException("No "+fieldName+" field in "+guestObject);
}
} else {
return f;
}
}
}