/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.api.debug.test;
import com.oracle.truffle.api.debug.Breakpoint;
import com.oracle.truffle.api.debug.DebugStackFrame;
import com.oracle.truffle.api.debug.DebugValue;
import com.oracle.truffle.api.debug.Debugger;
import com.oracle.truffle.api.debug.DebuggerSession;
import com.oracle.truffle.api.debug.SuspendedEvent;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.KeyInfo;
import com.oracle.truffle.api.interop.MessageResolution;
import com.oracle.truffle.api.interop.Resolve;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.java.JavaInterop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.vm.PolyglotEngine;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class DebugValueTest extends AbstractDebugTest {
@Test
public void testNumValue() throws Throwable {
final Source source = testSource("ROOT(\n" +
" VARIABLE(a, 42), \n" +
" VARIABLE(inf, infinity), \n" +
" STATEMENT()\n" +
")\n");
try (DebuggerSession session = startSession()) {
session.suspendNextExecution();
startEval(source);
expectSuspended((SuspendedEvent event) -> {
DebugStackFrame frame = event.getTopStackFrame();
DebugValue value42 = frame.getScope().getDeclaredValue("a");
assertEquals("a", value42.getName());
assertFalse(value42.isArray());
assertNull(value42.getArray());
assertNull(value42.getProperties());
assertEquals("Integer", value42.getMetaObject().as(String.class));
assertEquals("Infinity", frame.getScope().getDeclaredValue("inf").getMetaObject().as(String.class));
SourceSection integerSS = value42.getSourceLocation();
assertEquals("source integer", integerSS.getCode());
SourceSection infinitySS = frame.getScope().getDeclaredValue("inf").getSourceLocation();
assertEquals("source infinity", infinitySS.getCode());
});
expectDone();
}
}
/**
* Test of {@link DebugValue#isReadable()}, {@link DebugValue#isWritable()} and
* {@link DebugValue#isInternal()}.
*/
@Test
public void testValueAttributes() throws Throwable {
final Source source = testSource("DEFINE(function, ROOT(\n" +
" ARGUMENT(a), \n" +
" STATEMENT()\n" +
"))\n");
PolyglotEngine engine = PolyglotEngine.newBuilder().build();
engine.eval(source);
PolyglotEngine.Value functionValue = engine.findGlobalSymbol("function");
assertNotNull(functionValue);
Debugger debugger = Debugger.find(engine);
// Test of the default attribute values:
NoAttributesTruffleObject nao = new NoAttributesTruffleObject();
boolean[] suspended = new boolean[]{false};
DebuggerSession session = debugger.startSession((SuspendedEvent event) -> {
assertFalse(suspended[0]);
DebugValue value = event.getTopStackFrame().getScope().getDeclaredValue("a");
assertNotNull(value);
DebugValue attributesTOValue = value.getProperties().iterator().next();
assertEquals("property", attributesTOValue.getName());
// Property is readable by default
assertTrue(attributesTOValue.isReadable());
// Property is writable by default
assertTrue(attributesTOValue.isWritable());
// Property is not internal by default
assertFalse(attributesTOValue.isInternal());
event.prepareContinue();
suspended[0] = true;
});
session.install(Breakpoint.newBuilder(source).lineIs(3).build());
functionValue.execute(nao);
session.close();
assertTrue(suspended[0]);
// Test of the modified attribute values:
suspended[0] = false;
final ModifiableAttributesTruffleObject mao = new ModifiableAttributesTruffleObject();
session = debugger.startSession((SuspendedEvent event) -> {
assertFalse(suspended[0]);
DebugValue value = event.getTopStackFrame().getScope().getDeclaredValue("a");
assertNotNull(value);
DebugValue attributesTOValue = value.getProperties().iterator().next();
assertEquals("property", attributesTOValue.getName());
// All false initially
assertFalse(attributesTOValue.isReadable());
assertFalse(attributesTOValue.isWritable());
assertFalse(attributesTOValue.isInternal());
mao.setIsReadable(true);
attributesTOValue = value.getProperties().iterator().next();
assertTrue(attributesTOValue.isReadable());
mao.setIsWritable(true);
attributesTOValue = value.getProperties().iterator().next();
assertTrue(attributesTOValue.isWritable());
mao.setIsInternal(true);
attributesTOValue = value.getProperties().iterator().next();
assertTrue(attributesTOValue.isInternal());
event.prepareContinue();
suspended[0] = true;
});
session.install(Breakpoint.newBuilder(source).lineIs(3).build());
functionValue.execute(mao);
session.close();
assertTrue(suspended[0]);
}
static final class NoAttributesTruffleObject implements TruffleObject {
@Override
public ForeignAccess getForeignAccess() {
return NoAttributesMessageResolutionForeign.ACCESS;
}
public static boolean isInstance(TruffleObject obj) {
return obj instanceof NoAttributesTruffleObject;
}
@MessageResolution(receiverType = NoAttributesTruffleObject.class)
static final class NoAttributesMessageResolution {
@Resolve(message = "KEYS")
abstract static class NoAttributesKeysNode extends Node {
@SuppressWarnings("unused")
public Object access(NoAttributesTruffleObject ato) {
return new PropertyKeysTruffleObject();
}
}
@Resolve(message = "READ")
abstract static class NoAttributesReadNode extends Node {
@SuppressWarnings("unused")
public Object access(NoAttributesTruffleObject ato, String name) {
return "propertyValue";
}
}
}
}
static final class ModifiableAttributesTruffleObject implements TruffleObject {
private boolean isReadable;
private boolean isWritable;
private boolean isInternal;
@Override
public ForeignAccess getForeignAccess() {
return ModifiableAttributesMessageResolutionForeign.ACCESS;
}
public void setIsReadable(boolean isReadable) {
this.isReadable = isReadable;
}
public void setIsWritable(boolean isWritable) {
this.isWritable = isWritable;
}
public void setIsInternal(boolean isInternal) {
this.isInternal = isInternal;
}
public static boolean isInstance(TruffleObject obj) {
return obj instanceof ModifiableAttributesTruffleObject;
}
@MessageResolution(receiverType = ModifiableAttributesTruffleObject.class)
static final class ModifiableAttributesMessageResolution {
@Resolve(message = "KEYS")
abstract static class ModifiableAttributesKeysNode extends Node {
public Object access(ModifiableAttributesTruffleObject ato, boolean internal) {
if (internal || ato.isInternal == internal) {
return new PropertyKeysTruffleObject();
} else {
return JavaInterop.asTruffleObject(new Object[]{});
}
}
}
@Resolve(message = "READ")
abstract static class ModifiableAttributesReadNode extends Node {
@SuppressWarnings("unused")
public Object access(ModifiableAttributesTruffleObject ato, String name) {
return "propertyValue";
}
}
@Resolve(message = "KEY_INFO")
abstract static class ModifiableAttributesKeyInfoNode extends Node {
@SuppressWarnings("unused")
public int access(ModifiableAttributesTruffleObject ato, String propName) {
return KeyInfo.newBuilder().setReadable(ato.isReadable).setWritable(ato.isWritable).setInternal(ato.isInternal).build();
}
}
}
}
/**
* Truffle object representing property keys and having one property named "property".
*/
static final class PropertyKeysTruffleObject implements TruffleObject {
@Override
public ForeignAccess getForeignAccess() {
return PropertyKeysMessageResolutionForeign.ACCESS;
}
public static boolean isInstance(TruffleObject obj) {
return obj instanceof PropertyKeysTruffleObject;
}
@MessageResolution(receiverType = PropertyKeysTruffleObject.class)
static final class PropertyKeysMessageResolution {
@Resolve(message = "HAS_SIZE")
abstract static class PropertyKeysHasSizeNode extends Node {
@SuppressWarnings("unused")
public boolean access(PropertyKeysTruffleObject ato) {
return true;
}
}
@Resolve(message = "GET_SIZE")
abstract static class PropertyKeysGetSizeNode extends Node {
@SuppressWarnings("unused")
public int access(PropertyKeysTruffleObject ato) {
return 1;
}
}
@Resolve(message = "READ")
abstract static class PropertyKeysReadNode extends Node {
@SuppressWarnings("unused")
public Object access(PropertyKeysTruffleObject ato, int index) {
return "property";
}
}
}
}
}