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 protected final int registerNumber; 52 protected final Project project; 53 protected final SmaliMethod method; 54 protected final String type; 55 56 private EvaluationContext evaluationContext; 57 private Value value; 58 LazyValue(SmaliMethod method, Project project, int registerNumber, String type)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 create(@onnull SmaliMethod method, @Nonnull Project project, int registerNumber, @Nonnull String type)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 getNullableValue(boolean allowNull)105 protected T getNullableValue(boolean allowNull) { 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 if (!allowNull) { 113 throw new IllegalStateException("Can't create evaluation context"); 114 } 115 return null; 116 } 117 } 118 119 value = SmaliCodeFragmentFactory.evaluateRegister(evaluationContext, method, registerNumber, type); 120 evaluationContext = null; 121 } catch (EvaluateException ex) { 122 if (!allowNull) { 123 throw new IllegalStateException(ex); 124 } 125 return null; 126 } 127 } 128 return (T)value; 129 } 130 131 @Nonnull getValue()132 protected T getValue() { 133 T value = getNullableValue(false); 134 assert value != null; 135 return value; 136 } 137 138 @Override type()139 public Type type() { 140 return getValue().type(); 141 } 142 143 @Override virtualMachine()144 public VirtualMachine virtualMachine() { 145 if (evaluationContext != null) { 146 return ((VirtualMachineProxyImpl)evaluationContext.getDebugProcess().getVirtualMachineProxy()) 147 .getVirtualMachine(); 148 } else { 149 final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext(); 150 final DebugProcessImpl process = debuggerContext.getDebugProcess(); 151 if (process != null) { 152 return process.getVirtualMachineProxy().getVirtualMachine(); 153 } 154 } 155 return null; 156 } 157 setEvaluationContext(@onnull EvaluationContext evaluationContext)158 public void setEvaluationContext(@Nonnull EvaluationContext evaluationContext) { 159 this.evaluationContext = evaluationContext; 160 } 161 equals(Object obj)162 @Override public boolean equals(Object obj) { 163 Value value = getNullableValue(true); 164 if (value != null) { 165 return value.equals(obj); 166 } 167 return super.equals(obj); 168 } 169 hashCode()170 @Override public int hashCode() { 171 Value value = getNullableValue(true); 172 if (value != null) { 173 return value.hashCode(); 174 } 175 return super.hashCode(); 176 } 177 toString()178 @Override public String toString() { 179 Value value = getNullableValue(true); 180 if (value != null) { 181 return value.toString(); 182 } 183 return super.toString(); 184 } 185 } 186