1 /* 2 * Copyright 2016, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.smalidea.debugging.value; 33 34 import com.intellij.debugger.DebuggerManagerEx; 35 import com.intellij.debugger.engine.DebugProcessImpl; 36 import com.intellij.debugger.engine.evaluation.EvaluateException; 37 import com.intellij.debugger.engine.evaluation.EvaluationContext; 38 import com.intellij.debugger.impl.DebuggerContextImpl; 39 import com.intellij.debugger.jdi.VirtualMachineProxyImpl; 40 import com.intellij.openapi.project.Project; 41 import com.sun.jdi.Type; 42 import com.sun.jdi.Value; 43 import com.sun.jdi.VirtualMachine; 44 import org.jf.smalidea.debugging.SmaliCodeFragmentFactory; 45 import org.jf.smalidea.psi.impl.SmaliMethod; 46 47 import javax.annotation.Nonnull; 48 import javax.annotation.Nullable; 49 50 public class LazyValue<T extends Value> implements Value { 51 private final int registerNumber; 52 private final Project project; 53 private final SmaliMethod method; 54 private final String type; 55 56 private EvaluationContext evaluationContext; 57 private Value value; 58 59 public LazyValue(SmaliMethod method, Project project, int registerNumber, String type) { 60 this.method = method; 61 this.project = project; 62 this.registerNumber = registerNumber; 63 this.type = type; 64 } 65 66 public static LazyValue create(@Nonnull SmaliMethod method, @Nonnull Project project, int registerNumber, 67 @Nonnull String type) { 68 if (type.equals("B")) { 69 return new LazyByteValue(method, project, registerNumber, type); 70 } else if (type.equals("S")) { 71 return new LazyShortValue(method, project, registerNumber, type); 72 } else if (type.equals("J")) { 73 return new LazyLongValue(method, project, registerNumber, type); 74 } else if (type.equals("I")) { 75 return new LazyIntegerValue(method, project, registerNumber, type); 76 } else if (type.equals("F")) { 77 return new LazyFloatValue(method, project, registerNumber, type); 78 } else if (type.equals("D")) { 79 return new LazyDoubleValue(method, project, registerNumber, type); 80 } else if (type.equals("Z")) { 81 return new LazyBooleanValue(method, project, registerNumber, type); 82 } else if (type.equals("C")) { 83 return new LazyCharValue(method, project, registerNumber, type); 84 } else if (type.equals("V")) { 85 return new LazyVoidValue(method, project, registerNumber, type); 86 } else if (type.startsWith("[")) { 87 return new LazyArrayReference(method, project, registerNumber, type); 88 } else if (type.equals("Ljava/lang/String;")) { 89 return new LazyStringReference(method, project, registerNumber, type); 90 } else if (type.equals("Ljava/lang/Class;")) { 91 return new LazyClassObjectReference(method, project, registerNumber, type); 92 } else if (type.equals("Ljava/lang/ThreadGroup;")) { 93 return new LazyThreadGroupReference(method, project, registerNumber, type); 94 } else if (type.equals("Ljava/lang/Thread;")) { 95 return new LazyThreadReference(method, project, registerNumber, type); 96 } else if (type.equals("Ljava/lang/ClassLoader;")) { 97 return new LazyClassLoaderReference(method, project, registerNumber, type); 98 } else if (type.startsWith("L")) { 99 return new LazyObjectReference(method, project, registerNumber, type); 100 } 101 return new LazyValue(method, project, registerNumber, type); 102 } 103 104 @Nullable 105 private T getNullableValue() { 106 if (value == null) { 107 try { 108 if (evaluationContext == null) { 109 final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext(); 110 evaluationContext = debuggerContext.createEvaluationContext(); 111 if (evaluationContext == null) { 112 return null; 113 } 114 } 115 116 value = SmaliCodeFragmentFactory.evaluateRegister(evaluationContext, method, registerNumber, type); 117 evaluationContext = null; 118 } catch (EvaluateException ex) { 119 return null; 120 } 121 } 122 return (T)value; 123 } 124 125 @Nonnull 126 protected T getValue() { 127 T value = getNullableValue(); 128 assert value != null; 129 return value; 130 } 131 132 @Override 133 public Type type() { 134 return getValue().type(); 135 } 136 137 @Override 138 public VirtualMachine virtualMachine() { 139 if (evaluationContext != null) { 140 return ((VirtualMachineProxyImpl)evaluationContext.getDebugProcess().getVirtualMachineProxy()) 141 .getVirtualMachine(); 142 } else { 143 final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext(); 144 final DebugProcessImpl process = debuggerContext.getDebugProcess(); 145 if (process != null) { 146 return process.getVirtualMachineProxy().getVirtualMachine(); 147 } 148 } 149 return null; 150 } 151 152 public void setEvaluationContext(@Nonnull EvaluationContext evaluationContext) { 153 this.evaluationContext = evaluationContext; 154 } 155 156 @Override public boolean equals(Object obj) { 157 Value value = getNullableValue(); 158 if (value != null) { 159 return value.equals(obj); 160 } 161 return super.equals(obj); 162 } 163 164 @Override public int hashCode() { 165 Value value = getNullableValue(); 166 if (value != null) { 167 return value.hashCode(); 168 } 169 return super.hashCode(); 170 } 171 172 @Override public String toString() { 173 Value value = getNullableValue(); 174 if (value != null) { 175 return value.toString(); 176 } 177 return super.toString(); 178 } 179 } 180