[yaml] numeric value validation
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / impl / ClassLoadingUtils.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.debugger.impl;
17
18 import com.intellij.debugger.engine.DebugProcess;
19 import com.intellij.debugger.engine.JVMNameUtil;
20 import com.intellij.debugger.engine.evaluation.EvaluateException;
21 import com.intellij.debugger.engine.evaluation.EvaluationContext;
22 import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
23 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
24 import com.intellij.openapi.util.io.StreamUtil;
25 import com.intellij.rt.debugger.ImageSerializer;
26 import com.sun.jdi.*;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.List;
35
36 /**
37  * @author egor
38  */
39 public class ClassLoadingUtils {
40   private ClassLoadingUtils() {}
41
42   public static ClassLoaderReference getClassLoader(EvaluationContext context, DebugProcess process) throws EvaluateException {
43     try {
44       // TODO [egor]: cache?
45       ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
46       Method ctorMethod = loaderClass.concreteMethodByName(JVMNameUtil.CONSTRUCTOR_NAME, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
47       ClassLoaderReference reference = (ClassLoaderReference)process.newInstance(context, loaderClass, ctorMethod, Arrays
48         .asList(createURLArray(context), context.getClassLoader()));
49       DebuggerUtilsEx.keep(reference, context);
50       return reference;
51     }
52     catch (Exception e) {
53       throw new EvaluateException("Error creating evaluation class loader: " + e, e);
54     }
55   }
56
57   public static void defineClass(String name,
58                                  byte[] bytes,
59                                  EvaluationContext context,
60                                  DebugProcess process,
61                                  ClassLoaderReference classLoader) throws EvaluateException {
62     try {
63       VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
64       Method defineMethod =
65         ((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
66       StringReference nameObj = proxy.mirrorOf(name);
67       DebuggerUtilsEx.keep(nameObj, context);
68       process.invokeMethod(context, classLoader, defineMethod,
69                            Arrays.asList(nameObj,
70                                          mirrorOf(bytes, context, process),
71                                          proxy.mirrorOf(0),
72                                          proxy.mirrorOf(bytes.length)));
73     }
74     catch (Exception e) {
75       throw new EvaluateException("Error during class " + name + " definition: " + e, e);
76     }
77   }
78
79   /**
80    * Finds and if necessary defines helper class
81    * May modify class loader in evaluationContext
82    */
83   @Nullable
84   public static ClassType getHelperClass(String name, EvaluationContext evaluationContext, DebugProcess process) throws EvaluateException {
85     // TODO [egor]: cache and load in boostrap class loader
86     try {
87       ClassLoaderReference classLoader = evaluationContext.getClassLoader();
88       return (ClassType)process.findClass(evaluationContext, name, classLoader);
89     } catch (EvaluateException e) {
90       Throwable cause = e.getCause();
91       if (cause instanceof InvocationException) {
92         if ("java.lang.ClassNotFoundException".equals(((InvocationException)cause).exception().type().name())) {
93           // need to define
94           ClassLoaderReference classLoader = getClassLoader(evaluationContext, process);
95           InputStream stream = ImageSerializer.class.getResourceAsStream("/" + name.replaceAll("[.]", "/") + ".class");
96           try {
97             if (stream == null) return null;
98             defineClass(name, StreamUtil.loadFromStream(stream), evaluationContext, process, classLoader);
99             ((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader);
100             return (ClassType)process.findClass(evaluationContext, name, classLoader);
101           }
102           catch (IOException ioe) {
103             throw new EvaluateException("Unable to read " + name + " class bytes", ioe);
104           }
105           finally {
106             try {
107               if (stream != null) {
108                 stream.close();
109               }
110             }
111             catch (IOException ignored) {}
112           }
113         }
114       }
115       throw e;
116     }
117   }
118
119   private static ArrayReference createURLArray(EvaluationContext context)
120     throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
121     DebugProcess process = context.getDebugProcess();
122     ArrayType arrayType = (ArrayType)process.findClass(context, "java.net.URL[]", context.getClassLoader());
123     ArrayReference arrayRef = arrayType.newInstance(1);
124     DebuggerUtilsEx.keep(arrayRef, context);
125     ClassType classType = (ClassType)process.findClass(context, "java.net.URL", context.getClassLoader());
126     VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
127     StringReference url = proxy.mirrorOf("file:a");
128     DebuggerUtilsEx.keep(url, context);
129     ObjectReference reference = process.newInstance(context,
130                                                     classType,
131                                                     classType.concreteMethodByName(JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/lang/String;)V"),
132                                                     Collections.singletonList(url));
133     DebuggerUtilsEx.keep(reference, context);
134     arrayRef.setValues(Collections.singletonList(reference));
135     return arrayRef;
136   }
137
138   private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process)
139     throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
140     ArrayType arrayClass = (ArrayType)process.findClass(context, "byte[]", context.getClassLoader());
141     ArrayReference reference = process.newInstance(arrayClass, bytes.length);
142     DebuggerUtilsEx.keep(reference, context);
143     List<Value> mirrors = new ArrayList<>(bytes.length);
144     for (byte b : bytes) {
145       mirrors.add(((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).mirrorOf(b));
146     }
147     reference.setValues(mirrors);
148     return reference;
149   }
150 }