001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.resolve.constants;
018
019 import com.google.common.base.Function;
020 import com.google.common.collect.Sets;
021 import com.intellij.psi.tree.IElementType;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.jet.JetNodeTypes;
025 import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
026 import org.jetbrains.jet.lang.diagnostics.Diagnostic;
027 import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages;
028 import org.jetbrains.jet.lang.psi.JetConstantExpression;
029 import org.jetbrains.jet.lang.psi.JetElement;
030 import org.jetbrains.jet.lang.types.JetType;
031 import org.jetbrains.jet.lang.types.TypeConstructor;
032 import org.jetbrains.jet.lang.types.TypeUtils;
033 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035
036 import java.util.Set;
037
038 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
039
040 public class CompileTimeConstantResolver {
041 private final KotlinBuiltIns builtIns;
042
043 public CompileTimeConstantResolver() {
044 this.builtIns = KotlinBuiltIns.getInstance();
045 }
046
047 @Nullable
048 public Diagnostic checkConstantExpressionType(
049 @NotNull JetConstantExpression expression,
050 @NotNull JetType expectedType
051 ) {
052 CompileTimeConstant<?> compileTimeConstant = getCompileTimeConstant(expression, expectedType);
053 Set<DiagnosticFactory> errorsThatDependOnExpectedType =
054 Sets.<DiagnosticFactory>newHashSet(CONSTANT_EXPECTED_TYPE_MISMATCH, NULL_FOR_NONNULL_TYPE);
055
056 if (compileTimeConstant instanceof ErrorValueWithDiagnostic) {
057 Diagnostic diagnostic = ((ErrorValueWithDiagnostic) compileTimeConstant).getDiagnostic();
058 if (errorsThatDependOnExpectedType.contains(diagnostic.getFactory())) {
059 return diagnostic;
060 }
061 }
062 return null;
063 }
064
065 @NotNull
066 public CompileTimeConstant<?> getCompileTimeConstant(
067 @NotNull JetConstantExpression expression,
068 @NotNull JetType expectedType
069 ) {
070 IElementType elementType = expression.getNode().getElementType();
071
072 CompileTimeConstant<?> value;
073 if (elementType == JetNodeTypes.INTEGER_CONSTANT) {
074 value = getIntegerValue(expression, expectedType);
075 }
076 else if (elementType == JetNodeTypes.FLOAT_CONSTANT) {
077 value = getFloatValue(expression, expectedType);
078 }
079 else if (elementType == JetNodeTypes.BOOLEAN_CONSTANT) {
080 value = getBooleanValue(expression, expectedType);
081 }
082 else if (elementType == JetNodeTypes.CHARACTER_CONSTANT) {
083 value = getCharValue(expression, expectedType);
084 }
085 else if (elementType == JetNodeTypes.NULL) {
086 value = getNullValue(expression, expectedType);
087 }
088 else {
089 throw new IllegalArgumentException("Unsupported constant: " + expression);
090 }
091 return value;
092 }
093
094 @NotNull
095 public CompileTimeConstant<?> getIntegerValue(
096 @NotNull JetConstantExpression expression, @NotNull JetType expectedType
097 ) {
098 String text = expression.getText();
099 return getIntegerValue(parseLongValue(text), expectedType, expression);
100 }
101
102 @NotNull
103 public CompileTimeConstant<?> getIntegerValue(
104 @Nullable Long value,
105 @NotNull JetType expectedType,
106 @NotNull JetConstantExpression expression
107 ) {
108 if (value == null) {
109 return createErrorValue(INT_LITERAL_OUT_OF_RANGE.on(expression));
110 }
111 if (noExpectedTypeOrError(expectedType)) {
112 if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) {
113 return new IntValue(value.intValue());
114 }
115 return new LongValue(value);
116 }
117 Function<Long, ? extends CompileTimeConstant<?>> create;
118 long lowerBound;
119 long upperBound;
120 TypeConstructor constructor = expectedType.getConstructor();
121 if (constructor == builtIns.getInt().getTypeConstructor()) {
122 create = IntValue.CREATE;
123 lowerBound = Integer.MIN_VALUE;
124 upperBound = Integer.MAX_VALUE;
125 }
126 else if (constructor == builtIns.getLong().getTypeConstructor()) {
127 create = LongValue.CREATE;
128 lowerBound = Long.MIN_VALUE;
129 upperBound = Long.MAX_VALUE;
130 }
131 else if (constructor == builtIns.getShort().getTypeConstructor()) {
132 create = ShortValue.CREATE;
133 lowerBound = Short.MIN_VALUE;
134 upperBound = Short.MAX_VALUE;
135 }
136 else if (constructor == builtIns.getByte().getTypeConstructor()) {
137 create = ByteValue.CREATE;
138 lowerBound = Byte.MIN_VALUE;
139 upperBound = Byte.MAX_VALUE;
140 }
141 else {
142 JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
143 JetType intType = builtIns.getIntType();
144 JetType longType = builtIns.getLongType();
145 if (typeChecker.isSubtypeOf(intType, expectedType)) {
146 return getIntegerValue(value, intType, expression);
147 }
148 else if (typeChecker.isSubtypeOf(longType, expectedType)) {
149 return getIntegerValue(value, longType, expression);
150 }
151 else {
152 return createErrorValue(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "integer", expectedType));
153 }
154 }
155
156 if (value != null && lowerBound <= value && value <= upperBound) {
157 return create.apply(value);
158 }
159 return createErrorValue(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "integer", expectedType));
160 }
161
162 @Nullable
163 public static Long parseLongValue(String text) {
164 try {
165 long value;
166 if (text.startsWith("0x") || text.startsWith("0X")) {
167 String hexString = text.substring(2);
168 value = Long.parseLong(hexString, 16);
169 }
170 else if (text.startsWith("0b") || text.startsWith("0B")) {
171 String binString = text.substring(2);
172 value = Long.parseLong(binString, 2);
173 }
174 else {
175 value = Long.parseLong(text);
176 }
177 return value;
178 }
179 catch (NumberFormatException e) {
180 return null;
181 }
182 }
183
184 @Nullable
185 public static Double parseDoubleValue(String text) {
186 try {
187 return Double.parseDouble(text);
188 }
189 catch (NumberFormatException e) {
190 return null;
191 }
192 }
193
194 @NotNull
195 public CompileTimeConstant<?> getFloatValue(
196 @NotNull JetConstantExpression expression, @NotNull JetType expectedType
197 ) {
198 String text = expression.getText();
199 try {
200 if (noExpectedTypeOrError(expectedType)
201 || JetTypeChecker.INSTANCE.isSubtypeOf(builtIns.getDoubleType(), expectedType)) {
202 return new DoubleValue(Double.parseDouble(text));
203 }
204 else if (JetTypeChecker.INSTANCE.isSubtypeOf(builtIns.getFloatType(), expectedType)) {
205 return new FloatValue(Float.parseFloat(text));
206 }
207 else {
208 return createErrorValue(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "floating-point", expectedType));
209 }
210 }
211 catch (NumberFormatException e) {
212 return createErrorValue(FLOAT_LITERAL_OUT_OF_RANGE.on(expression));
213 }
214 }
215
216 @Nullable
217 private static CompileTimeConstant<?> checkNativeType(
218 JetType expectedType,
219 String title,
220 JetType nativeType,
221 JetConstantExpression expression
222 ) {
223 if (!noExpectedTypeOrError(expectedType)
224 && !JetTypeChecker.INSTANCE.isSubtypeOf(nativeType, expectedType)) {
225
226 return createErrorValue(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, title, expectedType));
227 }
228 return null;
229 }
230
231 @NotNull
232 public CompileTimeConstant<?> getBooleanValue(
233 @NotNull JetConstantExpression expression, @NotNull JetType expectedType
234 ) {
235 String text = expression.getText();
236 CompileTimeConstant<?> error = checkNativeType(expectedType, "boolean", builtIns.getBooleanType(), expression);
237 if (error != null) {
238 return error;
239 }
240 if ("true".equals(text)) {
241 return BooleanValue.TRUE;
242 }
243 else if ("false".equals(text)) {
244 return BooleanValue.FALSE;
245 }
246 throw new IllegalStateException("Must not happen. A boolean literal has text: " + text);
247 }
248
249 @NotNull
250 public CompileTimeConstant<?> getCharValue(
251 @NotNull JetConstantExpression expression, @NotNull JetType expectedType
252 ) {
253 String text = expression.getText();
254 CompileTimeConstant<?> error = checkNativeType(expectedType, "character", builtIns.getCharType(), expression);
255 if (error != null) {
256 return error;
257 }
258
259 // Strip the quotes
260 if (text.length() < 2 || text.charAt(0) != '\'' || text.charAt(text.length() - 1) != '\'') {
261 return createErrorValue(INCORRECT_CHARACTER_LITERAL.on(expression));
262 }
263 text = text.substring(1, text.length() - 1); // now there're no quotes
264
265 if (text.length() == 0) {
266 return createErrorValue(EMPTY_CHARACTER_LITERAL.on(expression));
267 }
268
269 if (text.charAt(0) != '\\') {
270 // No escape
271 if (text.length() == 1) {
272 return new CharValue(text.charAt(0));
273 }
274 return createErrorValue(TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL.on(expression, expression));
275 }
276 return escapedStringToCharValue(text, expression);
277 }
278
279 @NotNull
280 public static CompileTimeConstant<?> escapedStringToCharValue(
281 @NotNull String text,
282 @NotNull JetElement expression
283 ) {
284 assert text.length() > 0 && text.charAt(0) == '\\' : "Only escaped sequences must be passed to this routine: " + text;
285
286 // Escape
287 String escape = text.substring(1); // strip the slash
288 switch (escape.length()) {
289 case 0:
290 // bare slash
291 return illegalEscape(expression);
292 case 1:
293 // one-char escape
294 Character escaped = translateEscape(escape.charAt(0));
295 if (escaped == null) {
296 return illegalEscape(expression);
297 }
298 return new CharValue(escaped);
299 case 5:
300 // unicode escape
301 if (escape.charAt(0) == 'u') {
302 try {
303 Integer intValue = Integer.valueOf(escape.substring(1), 16);
304 return new CharValue((char) intValue.intValue());
305 } catch (NumberFormatException e) {
306 // Will be reported below
307 }
308 }
309 break;
310 }
311 return illegalEscape(expression);
312 }
313
314 @NotNull
315 private static CompileTimeConstant<?> illegalEscape(@NotNull JetElement expression) {
316 return createErrorValue(ILLEGAL_ESCAPE.on(expression, expression));
317 }
318
319 @Nullable
320 public static Character translateEscape(char c) {
321 switch (c) {
322 case 't':
323 return '\t';
324 case 'b':
325 return '\b';
326 case 'n':
327 return '\n';
328 case 'r':
329 return '\r';
330 case '\'':
331 return '\'';
332 case '\"':
333 return '\"';
334 case '\\':
335 return '\\';
336 case '$':
337 return '$';
338 }
339 return null;
340 }
341
342 @NotNull
343 public static CompileTimeConstant<?> getNullValue(@NotNull JetConstantExpression expression, @NotNull JetType expectedType) {
344 if (noExpectedTypeOrError(expectedType) || expectedType.isNullable()) {
345 return NullValue.NULL;
346 }
347 return createErrorValue(NULL_FOR_NONNULL_TYPE.on(expression, expectedType));
348 }
349
350 private static boolean noExpectedTypeOrError(JetType expectedType) {
351 return TypeUtils.noExpectedType(expectedType) || expectedType.isError();
352 }
353
354 @NotNull
355 private static ErrorValue createErrorValue(@NotNull Diagnostic diagnostic) {
356 return new ErrorValueWithDiagnostic(diagnostic);
357 }
358
359 public static class ErrorValueWithDiagnostic extends ErrorValue {
360 private final Diagnostic diagnostic;
361
362 public ErrorValueWithDiagnostic(@NotNull Diagnostic diagnostic) {
363 this.diagnostic = diagnostic;
364 }
365
366 @NotNull
367 public Diagnostic getDiagnostic() {
368 return diagnostic;
369 }
370
371 @NotNull
372 @Override
373 public JetType getType(@NotNull KotlinBuiltIns kotlinBuiltIns) {
374 throw new UnsupportedOperationException();
375 }
376
377 @Override
378 public String toString() {
379 return DefaultErrorMessages.RENDERER.render(diagnostic);
380 }
381 }
382 }