| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.lang; |
| |
| import static javaemul.internal.InternalPreconditions.checkType; |
| |
| import com.google.gwt.core.client.JavaScriptObject; |
| |
| import javaemul.internal.annotations.HasNoSideEffects; |
| |
| // CHECKSTYLE_NAMING_OFF: Uses legacy conventions of underscore prefixes. |
| |
| /** |
| * This is a magic class the compiler uses to perform any cast operations that require code.<br /> |
| * |
| * The cast operations are only as accurate as the contents of the castableTypeMaps and should not |
| * be used directly by user code. The compiler takes care to record most cast operations in user |
| * code so that it can build limited but accurate castableTypeMaps. |
| */ |
| final class Cast { |
| |
| /** |
| * As plain JavaScript Strings (not monkey patched) are used to model Java Strings, |
| * {@code stringCastMap} stores runtime type info for cast purposes for string objects. |
| * Same applies to Double and Boolean. |
| * |
| * NOTE: it is important that the field is left uninitialized so that Cast does not |
| * require a clinit. |
| */ |
| // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor |
| private static JavaScriptObject stringCastMap; |
| private static JavaScriptObject doubleCastMap; |
| private static JavaScriptObject booleanCastMap; |
| |
| @HasNoSideEffects |
| static native boolean canCast(Object src, JavaScriptObject dstId) /*-{ |
| if (@com.google.gwt.lang.Cast::instanceOfString(*)(src)) { |
| return !!@com.google.gwt.lang.Cast::stringCastMap[dstId]; |
| } else if (src.@java.lang.Object::castableTypeMap) { |
| return !!src.@java.lang.Object::castableTypeMap[dstId]; |
| } else if (@com.google.gwt.lang.Cast::instanceOfDouble(*)(src)) { |
| return !!@com.google.gwt.lang.Cast::doubleCastMap[dstId]; |
| } else if (@com.google.gwt.lang.Cast::instanceOfBoolean(*)(src)) { |
| return !!@com.google.gwt.lang.Cast::booleanCastMap[dstId]; |
| } |
| return false; |
| }-*/; |
| |
| @HasNoSideEffects |
| static native boolean canCastClass(Class<?> srcClazz, Class<?> dstClass) /*-{ |
| var srcTypeId = srcClazz.@java.lang.Class::typeId; |
| var dstTypeId = dstClass.@java.lang.Class::typeId; |
| var prototype = @com.google.gwt.lang.Runtime::prototypesByTypeId[srcTypeId]; |
| return @com.google.gwt.lang.Cast::canCast(*)(prototype, dstTypeId); |
| }-*/; |
| |
| static native String charToString(char x) /*-{ |
| return String.fromCharCode(x); |
| }-*/; |
| |
| static Object castTo(Object src, JavaScriptObject dstId) { |
| checkType(src == null || canCast(src, dstId)); |
| return src; |
| } |
| |
| // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor |
| static Object castToString(Object src) { |
| checkType(src == null || instanceOfString(src)); |
| return src; |
| } |
| |
| static Object castToDouble(Object src) { |
| checkType(src == null || instanceOfDouble(src)); |
| return src; |
| } |
| |
| static Object castToBoolean(Object src) { |
| checkType(src == null || instanceOfBoolean(src)); |
| return src; |
| } |
| |
| /** |
| * Allow a cast to an java.lang.Object array, accepting also untyped arrays. |
| */ |
| static Object castToArray(Object src) { |
| checkType(src == null || instanceOfArray(src)); |
| return src; |
| } |
| |
| /** |
| * Allow a cast to an array of Jsos, accepting also untyped arrays. |
| */ |
| static Object castToJsoArray(Object src, JavaScriptObject dstId) { |
| checkType(src == null || instanceOfJsoArray(src, dstId)); |
| return src; |
| } |
| |
| /** |
| * Allow a cast to (untyped) array. This case covers single and multidimensional JsType arrays. |
| */ |
| static Object castToJsArray(Object src) { |
| checkType(src == null || instanceOfJsArray(src)); |
| return src; |
| } |
| |
| /** |
| * Allow a dynamic cast to an object, always succeeding if it's a JSO. |
| */ |
| static Object castToAllowJso(Object src, JavaScriptObject dstId) { |
| checkType(src == null || isJavaScriptObject(src) || canCast(src, dstId)); |
| return src; |
| } |
| |
| /** |
| * Allow a cast to JSO only if there's no type ID. |
| */ |
| static Object castToJso(Object src) { |
| checkType(src == null || isJavaScriptObject(src)); |
| return src; |
| } |
| |
| /** |
| * Allow a dynamic cast to a JsFunction interface only if it is a function. |
| */ |
| static Object castToFunction(Object src) { |
| checkType(src == null || isFunction(src)); |
| return src; |
| } |
| |
| /** |
| * Allow a dynamic cast to a native GLOBAL.Object if it is JavaScript object. |
| */ |
| static Object castToJsObject(Object src) { |
| checkType(src == null || isJsObject(src)); |
| return src; |
| } |
| |
| /** |
| * A dynamic cast that optionally checks for JsType prototypes. |
| */ |
| static Object castToNative(Object src, JavaScriptObject jsType) { |
| checkType(src == null || jsinstanceOf(src, jsType)); |
| return src; |
| } |
| |
| static Object castToUnknownNative(Object src) { |
| return src; |
| } |
| |
| static boolean instanceOf(Object src, JavaScriptObject dstId) { |
| return (src != null) && canCast(src, dstId); |
| } |
| |
| @HasNoSideEffects |
| static native boolean instanceOfString(Object src) /*-{ |
| return typeof(src) === "string"; |
| }-*/; |
| |
| @HasNoSideEffects |
| static native boolean instanceOfDouble(Object src) /*-{ |
| return typeof(src) === "number"; |
| }-*/; |
| |
| @HasNoSideEffects |
| static native boolean instanceOfBoolean(Object src) /*-{ |
| return typeof(src) === "boolean"; |
| }-*/; |
| |
| /** |
| * Returns true if {@code src} is Java object array or an untyped array. |
| */ |
| static boolean instanceOfArray(Object src) { |
| return isArray(src) && !Array.isPrimitiveArray(src); |
| } |
| |
| /** |
| * Returns true if {@code src} is Java object array or an untyped array. |
| */ |
| static boolean instanceOfJsoArray(Object src, JavaScriptObject dstId) { |
| return canCast(src, dstId) || !Util.hasTypeMarker(src) && isArray(src); |
| } |
| |
| /** |
| * Returns true if {@code src} is an array (native or not). |
| */ |
| static boolean instanceOfJsArray(Object src) { |
| return isArray(src); |
| } |
| |
| static boolean instanceOfNative(Object src, JavaScriptObject jsType) { |
| return jsinstanceOf(src, jsType); |
| } |
| |
| static boolean instanceOfUnknownNative(Object src) { |
| return src != null; |
| } |
| |
| static boolean instanceOfJso(Object src) { |
| return (src != null) && isJavaScriptObject(src); |
| } |
| |
| /** |
| * Returns true if the object is a Java object and can be cast, or if it's a |
| * non-null JSO. |
| */ |
| static boolean instanceOfAllowJso(Object src, JavaScriptObject dstId) { |
| return (src != null) && (isJavaScriptObject(src) || canCast(src, dstId)); |
| } |
| |
| /** |
| * Returns true if the object is a function. |
| */ |
| static boolean instanceOfFunction(Object src) { |
| return (src != null) && isFunction(src); |
| } |
| |
| /** |
| * Returns true if the object is a JS object. |
| */ |
| static boolean instanceOfJsObject(Object src) { |
| return (src != null) && isJsObject(src); |
| } |
| |
| /** |
| * Returns whether the Object is a function. |
| */ |
| @HasNoSideEffects |
| private static native boolean isFunction(Object src)/*-{ |
| return typeof(src) === "function"; |
| }-*/; |
| |
| @HasNoSideEffects |
| private static native boolean isJsObject(Object src)/*-{ |
| return typeof(src) === "object" || typeof (src) == "function"; |
| }-*/; |
| |
| @HasNoSideEffects |
| static boolean isJavaScriptObject(Object src) { |
| // TODO(rluble): should return false to for plain arrays. |
| return isJsObjectOrFunction(src) && !Util.hasTypeMarker(src); |
| } |
| |
| @HasNoSideEffects |
| private static native boolean isJsObjectOrFunction(Object src) /*-{ |
| return typeof(src) === "object" || typeof(src) === "function"; |
| }-*/; |
| |
| /** |
| * Uses the not operator to perform a null-check; do NOT use on anything that |
| * could be a String, 'unboxed' Double, or 'unboxed' Boolean. |
| */ |
| static native boolean isNotNull(Object src) /*-{ |
| // Coerce to boolean. |
| return !!src; |
| }-*/; |
| |
| /** |
| * Uses the not operator to perform a null-check; do NOT use on anything that |
| * could be a String, 'unboxed' Double, or 'unboxed' Boolean. |
| */ |
| static native boolean isNull(Object src) /*-{ |
| return !src; |
| }-*/; |
| |
| static native boolean jsEquals(Object a, Object b) /*-{ |
| return a == b; |
| }-*/; |
| |
| /** |
| * Determine if object is an instanceof jsType regardless of window or frame. |
| */ |
| @HasNoSideEffects |
| private static native boolean jsinstanceOf(Object obj, JavaScriptObject jsType) /*-{ |
| return obj && jsType && obj instanceof jsType; |
| }-*/; |
| |
| static native boolean jsNotEquals(Object a, Object b) /*-{ |
| return a != b; |
| }-*/; |
| |
| static native Object maskUndefined(Object src) /*-{ |
| return (src == null) ? null : src; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3. |
| */ |
| static native byte narrow_byte(double x) /*-{ |
| return x << 24 >> 24; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3. |
| */ |
| static native char narrow_char(double x) /*-{ |
| return x & 0xFFFF; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3. |
| */ |
| static native int narrow_int(double x) /*-{ |
| return x | 0; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3. |
| */ |
| static native short narrow_short(double x) /*-{ |
| return x << 16 >> 16; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3 for why we do a two-step cast. First we round to int, then |
| * narrow to byte. |
| */ |
| static byte round_byte(double x) { |
| return narrow_byte(round_int(x)); |
| } |
| |
| /** |
| * See JLS 5.1.3 for why we do a two-step cast. First we round to int, then |
| * narrow to char. |
| */ |
| static char round_char(double x) { |
| return narrow_char(round_int(x)); |
| } |
| |
| /** |
| * See JLS 5.1.3. |
| */ |
| static native int round_int(double x) /*-{ |
| // TODO: reference java.lang.Integer::MAX_VALUE when we get clinits fixed |
| return Math.max(Math.min(x, 2147483647), -2147483648) | 0; |
| }-*/; |
| |
| /** |
| * See JLS 5.1.3 for why we do a two-step cast. First we rount to int, then |
| * narrow to short. |
| */ |
| static short round_short(double x) { |
| return narrow_short(round_int(x)); |
| } |
| |
| /** |
| * Check a statically false cast, which can succeed if the argument is null. |
| * Called by compiler-generated code based on static type information. |
| */ |
| static Object throwClassCastExceptionUnlessNull(Object o) throws ClassCastException { |
| checkType(o == null); |
| return o; |
| } |
| |
| /** |
| * Returns true if Object can dispatch instance methods and does not need a compiler |
| * provided trampoline. |
| * |
| * Java non primitive objects fall into 3 classes: Strings, arrays (of primitive or non primitive |
| * types) and regular Java Objects (all others). |
| * |
| * Only regular Java object have dynamic instance dispatch, strings and arrays need compiler |
| * generated trampolines to implement instance dispatch. |
| */ |
| static boolean hasJavaObjectVirtualDispatch(Object src) { |
| return !isArray(src) && Util.hasTypeMarker(src); |
| } |
| |
| @HasNoSideEffects |
| /** |
| * Returns true if {@code src} is a Java array. |
| */ |
| static native boolean isArray(Object src) /*-{ |
| return Array.isArray(src); |
| }-*/; |
| |
| @HasNoSideEffects |
| static native Class<?> getClass(Object array) /*-{ |
| return array.@java.lang.Object::___clazz |
| || Array.isArray(array) && @JavaScriptObject[]::class |
| || @JavaScriptObject::class; |
| }-*/; |
| } |
| |
| // CHECKSTYLE_NAMING_ON |