/* * 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