| /******************************************************************************* |
| * Copyright (c) 2000, 2012 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.eval.ast.instructions; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| |
| /** |
| * Copy of org.eclipse.jdt.core.Signature. The class is copied here solely for |
| * the purpose of commenting out the line: CharOperation.replace(result, |
| * C_DOLLAR, C_DOT); in the method toCharArray(char[]). See Bug 22165 |
| */ |
| public class RuntimeSignature { |
| public static final char C_BOOLEAN = 'Z'; |
| public static final char C_BYTE = 'B'; |
| public static final char C_CHAR = 'C'; |
| public static final char C_DOUBLE = 'D'; |
| public static final char C_FLOAT = 'F'; |
| public static final char C_INT = 'I'; |
| public static final char C_SEMICOLON = ';'; |
| public static final char C_LONG = 'J'; |
| public static final char C_SHORT = 'S'; |
| public static final char C_VOID = 'V'; |
| public static final char C_DOT = '.'; |
| public static final char C_DOLLAR = '$'; |
| public static final char C_ARRAY = '['; |
| public static final char C_RESOLVED = 'L'; |
| public static final char C_UNRESOLVED = 'Q'; |
| public static final char C_NAME_END = ';'; |
| public static final char C_PARAM_START = '('; |
| public static final char C_PARAM_END = ')'; |
| public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$ |
| public static final String SIG_BYTE = "B"; //$NON-NLS-1$ |
| public static final String SIG_CHAR = "C"; //$NON-NLS-1$ |
| public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$ |
| public static final String SIG_FLOAT = "F"; //$NON-NLS-1$ |
| public static final String SIG_INT = "I"; //$NON-NLS-1$ |
| public static final String SIG_LONG = "J"; //$NON-NLS-1$ |
| public static final String SIG_SHORT = "S"; //$NON-NLS-1$ |
| public static final String SIG_VOID = "V"; //$NON-NLS-1$ |
| private static final char[] NO_CHAR = new char[0]; |
| private static final char[] BOOLEAN = { 'b', 'o', 'o', 'l', 'e', 'a', 'n' }; |
| private static final char[] BYTE = { 'b', 'y', 't', 'e' }; |
| private static final char[] CHAR = { 'c', 'h', 'a', 'r' }; |
| private static final char[] DOUBLE = { 'd', 'o', 'u', 'b', 'l', 'e' }; |
| private static final char[] FLOAT = { 'f', 'l', 'o', 'a', 't' }; |
| private static final char[] INT = { 'i', 'n', 't' }; |
| private static final char[] LONG = { 'l', 'o', 'n', 'g' }; |
| private static final char[] SHORT = { 's', 'h', 'o', 'r', 't' }; |
| private static final char[] VOID = { 'v', 'o', 'i', 'd' }; |
| |
| public static String toString(String signature) |
| throws IllegalArgumentException { |
| return new String(toCharArray(signature.toCharArray())); |
| } |
| |
| public static char[] toCharArray(char[] signature) |
| throws IllegalArgumentException { |
| try { |
| int sigLength = signature.length; |
| |
| if (sigLength == 0 || signature[0] == C_PARAM_START) { |
| return toCharArray(signature, NO_CHAR, null, true, true); |
| } |
| |
| // compute result length |
| int resultLength = 0; |
| int index = -1; |
| while (signature[++index] == C_ARRAY) { |
| resultLength += 2; // [] |
| } |
| switch (signature[index]) { |
| case C_BOOLEAN: |
| resultLength += BOOLEAN.length; |
| break; |
| case C_BYTE: |
| resultLength += BYTE.length; |
| break; |
| case C_CHAR: |
| resultLength += CHAR.length; |
| break; |
| case C_DOUBLE: |
| resultLength += DOUBLE.length; |
| break; |
| case C_FLOAT: |
| resultLength += FLOAT.length; |
| break; |
| case C_INT: |
| resultLength += INT.length; |
| break; |
| case C_LONG: |
| resultLength += LONG.length; |
| break; |
| case C_SHORT: |
| resultLength += SHORT.length; |
| break; |
| case C_VOID: |
| resultLength += VOID.length; |
| break; |
| case C_RESOLVED: |
| case C_UNRESOLVED: |
| int end = CharOperation.indexOf(C_SEMICOLON, signature, index); |
| if (end == -1) |
| throw new IllegalArgumentException(); |
| int start = index + 1; |
| resultLength += end - start; |
| break; |
| default: |
| throw new IllegalArgumentException(); |
| } |
| |
| char[] result = new char[resultLength]; |
| copyType(signature, 0, result, 0, true); |
| |
| /** |
| * Converts '$' separated type signatures into '.' separated type |
| * signature. NOTE: This assumes that the type signature is an inner |
| * type signature. This is true in most cases, but someone can |
| * define a non-inner type name containing a '$'. However to tell |
| * the difference, we would have to resolve the signature, which |
| * cannot be done at this point. |
| */ |
| // CharOperation.replace(result, C_DOLLAR, C_DOT); |
| |
| return result; |
| } catch (ArrayIndexOutOfBoundsException e) { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| public static char[] toCharArray(char[] methodSignature, char[] methodName, |
| char[][] parameterNames, boolean fullyQualifyTypeNames, |
| boolean includeReturnType) { |
| try { |
| int firstParen = CharOperation.indexOf(C_PARAM_START, |
| methodSignature); |
| if (firstParen == -1) |
| throw new IllegalArgumentException(); |
| |
| int sigLength = methodSignature.length; |
| |
| // compute result length |
| |
| // method signature |
| int paramCount = 0; |
| int lastParen = -1; |
| int resultLength = 0; |
| signature: for (int i = firstParen; i < sigLength; i++) { |
| switch (methodSignature[i]) { |
| case C_ARRAY: |
| resultLength += 2; // [] |
| continue signature; |
| case C_BOOLEAN: |
| resultLength += BOOLEAN.length; |
| break; |
| case C_BYTE: |
| resultLength += BYTE.length; |
| break; |
| case C_CHAR: |
| resultLength += CHAR.length; |
| break; |
| case C_DOUBLE: |
| resultLength += DOUBLE.length; |
| break; |
| case C_FLOAT: |
| resultLength += FLOAT.length; |
| break; |
| case C_INT: |
| resultLength += INT.length; |
| break; |
| case C_LONG: |
| resultLength += LONG.length; |
| break; |
| case C_SHORT: |
| resultLength += SHORT.length; |
| break; |
| case C_VOID: |
| resultLength += VOID.length; |
| break; |
| case C_RESOLVED: |
| case C_UNRESOLVED: |
| int end = CharOperation.indexOf(C_SEMICOLON, |
| methodSignature, i); |
| if (end == -1) |
| throw new IllegalArgumentException(); |
| int start; |
| if (fullyQualifyTypeNames) { |
| start = i + 1; |
| } else { |
| start = CharOperation.lastIndexOf(C_DOT, |
| methodSignature, i, end) + 1; |
| if (start == 0) |
| start = i + 1; |
| } |
| resultLength += end - start; |
| i = end; |
| break; |
| case C_PARAM_START: |
| // add space for "(" |
| resultLength++; |
| continue signature; |
| case C_PARAM_END: |
| lastParen = i; |
| if (includeReturnType) { |
| if (paramCount > 0) { |
| // remove space for ", " that was added with last |
| // parameter and remove space that is going to be |
| // added for ", " after return type |
| // and add space for ") " |
| resultLength -= 2; |
| } // else |
| // remove space that is going to be added for ", " |
| // after return type |
| // and add space for ") " |
| // -> noop |
| |
| // decrement param count because it is going to be added |
| // for return type |
| paramCount--; |
| continue signature; |
| } |
| if (paramCount > 0) { |
| // remove space for ", " that was added with last |
| // parameter and add space for ")" |
| resultLength--; |
| } else { |
| // add space for ")" |
| resultLength++; |
| } |
| break signature; |
| default: |
| throw new IllegalArgumentException(); |
| } |
| resultLength += 2; // add space for ", " |
| paramCount++; |
| } |
| |
| // parameter names |
| if(parameterNames != null) { |
| for (char[] parameterName : parameterNames) { |
| resultLength += parameterName.length + 1; |
| // parameter name + space |
| } |
| } |
| |
| // selector |
| int selectorLength = methodName == null ? 0 : methodName.length; |
| resultLength += selectorLength; |
| |
| // create resulting char array |
| char[] result = new char[resultLength]; |
| |
| // returned type |
| int index = 0; |
| if (includeReturnType) { |
| long pos = copyType(methodSignature, lastParen + 1, result, |
| index, fullyQualifyTypeNames); |
| index = (int) (pos >>> 32); |
| result[index++] = ' '; |
| } |
| |
| // selector |
| if (methodName != null) { |
| System.arraycopy(methodName, 0, result, index, selectorLength); |
| index += selectorLength; |
| } |
| |
| // parameters |
| result[index++] = C_PARAM_START; |
| int sigPos = firstParen + 1; |
| for (int i = 0; i < paramCount; i++) { |
| long pos = copyType(methodSignature, sigPos, result, index, |
| fullyQualifyTypeNames); |
| index = (int) (pos >>> 32); |
| sigPos = (int) pos; |
| if (parameterNames != null) { |
| result[index++] = ' '; |
| char[] parameterName = parameterNames[i]; |
| int paramLength = parameterName.length; |
| System.arraycopy(parameterName, 0, result, index, |
| paramLength); |
| index += paramLength; |
| } |
| if (i != paramCount - 1) { |
| result[index++] = ','; |
| result[index++] = ' '; |
| } |
| } |
| if (sigPos >= sigLength) { |
| throw new IllegalArgumentException(); |
| // should be on last paren |
| } |
| result[index++] = C_PARAM_END; |
| |
| return result; |
| } catch (ArrayIndexOutOfBoundsException e) { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| private static long copyType(char[] signature, int sigPos, char[] dest, |
| int index, boolean fullyQualifyTypeNames) { |
| int arrayCount = 0; |
| loop: while (true) { |
| switch (signature[sigPos++]) { |
| case C_ARRAY: |
| arrayCount++; |
| break; |
| case C_BOOLEAN: |
| int length = BOOLEAN.length; |
| System.arraycopy(BOOLEAN, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_BYTE: |
| length = BYTE.length; |
| System.arraycopy(BYTE, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_CHAR: |
| length = CHAR.length; |
| System.arraycopy(CHAR, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_DOUBLE: |
| length = DOUBLE.length; |
| System.arraycopy(DOUBLE, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_FLOAT: |
| length = FLOAT.length; |
| System.arraycopy(FLOAT, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_INT: |
| length = INT.length; |
| System.arraycopy(INT, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_LONG: |
| length = LONG.length; |
| System.arraycopy(LONG, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_SHORT: |
| length = SHORT.length; |
| System.arraycopy(SHORT, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_VOID: |
| length = VOID.length; |
| System.arraycopy(VOID, 0, dest, index, length); |
| index += length; |
| break loop; |
| case C_RESOLVED: |
| case C_UNRESOLVED: |
| int end = CharOperation.indexOf(C_SEMICOLON, signature, sigPos); |
| if (end == -1) |
| throw new IllegalArgumentException(); |
| int start; |
| if (fullyQualifyTypeNames) { |
| start = sigPos; |
| } else { |
| start = CharOperation.lastIndexOf(C_DOT, signature, sigPos, |
| end) + 1; |
| if (start == 0) |
| start = sigPos; |
| } |
| length = end - start; |
| System.arraycopy(signature, start, dest, index, length); |
| sigPos = end + 1; |
| index += length; |
| break loop; |
| } |
| } |
| while (arrayCount-- > 0) { |
| dest[index++] = '['; |
| dest[index++] = ']'; |
| } |
| return (((long) index) << 32) + sigPos; |
| } |
| } |