Implement internal annotation @DoNotAutobox.
Change-Id: I7760fee29392237c335ddc46fa8b276c86f48bcd
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
index fb03ec8..06e96a8 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
@@ -31,6 +31,8 @@
import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
import org.eclipse.jdt.internal.compiler.impl.StringConstant;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
@@ -40,6 +42,7 @@
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import java.util.Arrays;
import java.util.Collections;
@@ -445,4 +448,74 @@
}
});
}
+
+ /*
+ * Implicit conversion helpers.
+ */
+
+ public static boolean requiresBoxing(int implicitConversion) {
+ return implicitConversion != -1
+ && (implicitConversion & TypeIds.BOXING) != 0;
+ }
+
+ public static boolean requiresUnboxing(int implicitConversion) {
+ return implicitConversion != -1
+ && (implicitConversion & TypeIds.UNBOXING) != 0;
+ }
+
+ public static BaseTypeBinding getBoxingPrimitiveType(ClassScope scope, int implicitConversion) {
+ int typeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
+ return getBaseTypeBinding(scope, typeId);
+ }
+
+ public static BaseTypeBinding getUnboxingPrimitiveType(
+ ClassScope scope, int implicitConversion) {
+ if (needsCastBeforeUnbox(scope, implicitConversion)) {
+ int typeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
+ return getBaseTypeBinding(scope, typeId);
+ }
+ int compileTypeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
+ return getBaseTypeBinding(scope, compileTypeId);
+ }
+
+ public static BaseTypeBinding getBaseTypeBinding(ClassScope scope, int typeId) {
+ return (BaseTypeBinding) TypeBinding.wellKnownType(scope, typeId);
+ }
+
+ public static ReferenceBinding getBoxedTypeBinding(
+ ClassScope scope, BaseTypeBinding primitiveType) {
+ return (ReferenceBinding) scope.boxing(primitiveType);
+ }
+
+ public static boolean needsCastBeforeUnbox(ClassScope scope, int implicitConversion) {
+ // values of specific types like j.l.Object need casting before auto unboxing.
+ int compileTypeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
+ return !(TypeBinding.wellKnownType(scope, compileTypeId) instanceof BaseTypeBinding);
+ }
+
+ private static final String VALUE_SUFFIX = "Value";
+ private static final String VALUE_OF_METHOD_NAME = "valueOf";
+
+ private static final char[] VALUE_SUFFIX_ = VALUE_SUFFIX.toCharArray();
+ private static final char[] VALUE_OF_ = VALUE_OF_METHOD_NAME.toCharArray();
+
+ public static MethodBinding getBoxingMethodBinding(
+ ClassScope scope, BaseTypeBinding primitiveType) {
+ ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
+ MethodBinding valueOfMethod = boxType.getExactMethod(VALUE_OF_,
+ new TypeBinding[]{primitiveType}, scope.compilationUnitScope());
+ assert valueOfMethod != null;
+ return valueOfMethod;
+ }
+
+ public static MethodBinding getUnboxingMethodBinding(
+ ClassScope scope, BaseTypeBinding primitiveType) {
+ ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
+ char[] selector = CharOperation.concat(primitiveType.simpleName, VALUE_SUFFIX_);
+
+ MethodBinding valueMethod =
+ boxType.getExactMethod(selector, new TypeBinding[0], scope.compilationUnitScope());
+ assert valueMethod != null;
+ return valueMethod;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
index 46bc6f5..aae771d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
+++ b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
@@ -368,12 +368,18 @@
public static final MockJavaResource SPECIALIZE_METHOD =
createMockJavaResource("javaemul.internal.annotations.SpecializeMethod",
"package javaemul.internal.annotations;",
- "public @interface SpecializeMethod {\n",
- " Class<?>[] params();\n" +
- " String target();\n",
+ "public @interface SpecializeMethod {",
+ " Class<?>[] params();",
+ " String target();",
"}"
);
+ public static final MockJavaResource DO_NOT_AUTOBOX =
+ createMockJavaResource("javaemul.internal.annotations.DoNotAutobox",
+ "package javaemul.internal.annotations;",
+ "public @interface DoNotAutobox {\n",
+ "}"
+ );
// TODO: move JS* annotations to intrinsic mock resource base
public static final MockJavaResource JSTYPE =
createMockJavaResource("jsinterop.annotations.JsType",
@@ -438,8 +444,8 @@
ERROR, FUNCTIONALINTERFACE, FLOAT, INTEGER, IS_SERIALIZABLE, JAVASCRIPTEXCEPTION,
JAVASCRIPTOBJECT, LIST, LONG, MAP, NO_CLASS_DEF_FOUND_ERROR, NUMBER, OBJECT,
RUNTIME_EXCEPTION, SERIALIZABLE, SHORT, STRING, STRING_BUILDER, SUPPRESS_WARNINGS, SYSTEM,
- THROWABLE, SPECIALIZE_METHOD, JSTYPE, JSCONSTRUCTOR, JSPACKAGE, JSPROPERTY, JSMETHOD,
- JSIGNORE, JSFUNCTION, JSOVERLAY, JSOPTIONAL};
+ THROWABLE, SPECIALIZE_METHOD, DO_NOT_AUTOBOX, JSTYPE, JSCONSTRUCTOR, JSPACKAGE, JSPROPERTY,
+ JSMETHOD, JSIGNORE, JSFUNCTION, JSOVERLAY, JSOPTIONAL};
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index ba927d8..266a53a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -125,7 +125,6 @@
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import com.google.gwt.util.regexfilter.WhitelistRegexFilter;
-import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -379,7 +378,7 @@
public void endVisit(AllocationExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
- List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
+ List<JExpression> arguments = popCallArguments(info, x.arguments, x.binding);
pushNewExpression(info, x, null, arguments, scope);
} catch (Throwable e) {
throw translateException(x, e);
@@ -405,7 +404,7 @@
if (x.initializer != null) {
// handled by ArrayInitializer.
} else {
- List<JExpression> dims = pop(x.dimensions);
+ List<JExpression> dims = performBoxUnboxConversions(pop(x.dimensions), x.dimensions);
push(JNewArray.createArrayWithDimensionExpressions(info, type, dims));
}
} catch (Throwable e) {
@@ -418,7 +417,8 @@
try {
SourceInfo info = makeSourceInfo(x);
JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
- List<JExpression> expressions = pop(x.expressions);
+ List<JExpression> expressions =
+ performBoxUnboxConversions(pop(x.expressions), x.expressions);
push(JNewArray.createArrayWithInitializers(info, type, expressions));
} catch (Throwable e) {
throw translateException(x, e);
@@ -773,7 +773,7 @@
JConstructor ctor = (JConstructor) typeMap.get(x.binding);
JExpression trueQualifier = makeThisRef(info);
JMethodCall call = new JMethodCall(info, trueQualifier, ctor);
- List<JExpression> callArgs = popCallArgs(info, x.arguments, x.binding);
+ List<JExpression> callArgs = popCallArguments(info, x.arguments, x.binding);
if (curClass.classType.isEnumOrSubclass() != null) {
// Enums: wire up synthetic name/ordinal params to the super method.
@@ -1367,7 +1367,7 @@
// Deal with any boxing/unboxing needed
JNode node = pop();
if (node instanceof JExpression) {
- node = simplify((JExpression) node, (Expression) x.body);
+ node = maybeBoxOrUnbox((JExpression) node, (Expression) x.body);
}
JMethodBody body = (JMethodBody) curMethod.method.getBody();
@@ -1525,7 +1525,7 @@
SourceInfo info = makeSourceInfo(x);
JMethod method = typeMap.get(x.binding);
- List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
+ List<JExpression> arguments = popCallArguments(info, x.arguments, x.binding);
JExpression receiver = pop(x.receiver);
if (x.receiver instanceof ThisReference) {
if (method.isStatic()) {
@@ -1560,7 +1560,7 @@
if (x.valueCast != null) {
JType[] targetTypes = processCastType(x.valueCast);
push(isUncheckedGenericMethodCall(x)
- ? maybeInsertUnsafeTypeCoersion(targetTypes[0], methodCall)
+ ? maybeInsertUnsafeTypeCoercion(targetTypes[0], methodCall)
: maybeCast(targetTypes, methodCall));
} else {
push(methodCall);
@@ -1649,7 +1649,7 @@
public void endVisit(QualifiedAllocationExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
- List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
+ List<JExpression> arguments = popCallArguments(info, x.arguments, x.binding);
pushNewExpression(info, x, x.enclosingInstance(), arguments, scope);
} catch (Throwable e) {
throw translateException(x, e);
@@ -1939,7 +1939,7 @@
if (samMethod.getType() != JPrimitiveType.VOID) {
JExpression samExpression = boxOrUnboxExpression(samCall, referredMethodBinding.returnType,
declarationSamBinding.returnType);
- samMethodBody.getBlock().addStmt(simplify(samExpression, x).makeReturnStatement());
+ samMethodBody.getBlock().addStmt(maybeBoxOrUnbox(samExpression, x).makeReturnStatement());
} else {
samMethodBody.getBlock().addStmt(samCall.makeStatement());
}
@@ -1985,15 +1985,11 @@
}
if (fromType.isBaseType() && !toType.isBaseType()) {
- int implicitConversion = (fromType.id & TypeIds.IMPLICIT_CONVERSION_MASK) << 4;
- implicitConversion = implicitConversion | TypeIds.BOXING;
- return box(expr, implicitConversion);
+ return box(expr, JdtUtil.getBaseTypeBinding(curClass.scope, fromType.id));
}
if (!fromType.isBaseType() && toType.isBaseType()) {
- int implicitConversion = (toType.id & TypeIds.IMPLICIT_CONVERSION_MASK) << 4;
- implicitConversion = implicitConversion | TypeIds.UNBOXING;
- return unbox(expr, implicitConversion);
+ return unbox(expr, JdtUtil.getBaseTypeBinding(curClass.scope, toType.id));
}
TypeBinding castToType = fromType.genericCast(toType);
@@ -2585,23 +2581,26 @@
assert x instanceof NameReference;
return null;
}
- result = simplify(result, x);
+ result = maybeBoxOrUnbox(result, x);
+ return result;
+ }
+
+ protected <T extends JExpression> List<T> performBoxUnboxConversions(
+ List<T> result, Expression[] expressions) {
+ for (int i = 0; i < result.size(); i++) {
+ result.set(i, (T) maybeBoxOrUnbox(result.get(i), expressions[i]));
+ }
return result;
}
@SuppressWarnings("unchecked")
- protected <T extends JExpression> List<T> pop(Expression[] expressions) {
+ protected List<JExpression> pop(Expression[] expressions) {
if (expressions == null) {
return Collections.emptyList();
}
- List<T> result = (List<T>) popList(Collections2.filter(Arrays.asList(expressions),
+ return (List<JExpression>) popList(Collections2.filter(Arrays.asList(expressions),
Predicates.notNull()).size());
-
- for (int i = 0; i < result.size(); i++) {
- result.set(i, (T) simplify(result.get(i), expressions[i]));
- }
- return result;
}
protected JDeclarationStatement pop(LocalDeclaration decl) {
@@ -2611,7 +2610,7 @@
protected JStatement pop(Statement x) {
JNode pop = (x == null) ? null : pop();
if (x instanceof Expression) {
- return simplify((JExpression) pop, (Expression) x).makeStatement();
+ return maybeBoxOrUnbox((JExpression) pop, (Expression) x).makeStatement();
}
return (JStatement) pop;
}
@@ -2628,7 +2627,8 @@
if (element == null) {
it.remove();
} else if (element instanceof JExpression) {
- it.set((T) simplify((JExpression) element, (Expression) statements[i]).makeStatement());
+ it.set((T)
+ maybeBoxOrUnbox((JExpression) element, (Expression) statements[i]).makeStatement());
}
}
return result;
@@ -2756,25 +2756,47 @@
new JBinaryOperation(info, lhs.getType(), JBinaryOperator.ASG, lhs, param.makeRef(info));
}
- private JExpression box(JExpression original, int implicitConversion) {
- int typeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
- ClassScope scope = curClass.scope;
- BaseTypeBinding primitiveType = (BaseTypeBinding) TypeBinding.wellKnownType(scope, typeId);
- ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
- MethodBinding valueOfMethod = boxType.getExactMethod(VALUE_OF_,
- new TypeBinding[]{primitiveType}, scope.compilationUnitScope());
- assert valueOfMethod != null;
+ private JExpression box(JExpression original, BaseTypeBinding primitiveType) {
+ return box(original, primitiveType, false);
+ }
+ private JExpression box(
+ JExpression original, BaseTypeBinding primitiveType, boolean doNotAutobox) {
// Add a cast to the correct primitive type if needed.
JType targetPrimitiveType = typeMap.get(primitiveType);
if (original.getType() != targetPrimitiveType) {
original = new JCastOperation(original.getSourceInfo(), targetPrimitiveType, original);
}
- JMethod boxMethod = typeMap.get(valueOfMethod);
- JMethodCall call = new JMethodCall(original.getSourceInfo(), null, boxMethod);
- call.addArg(original);
- return call;
+ if (doNotAutobox) {
+ // Protect the primitive @DoNotAutobox values from optimizations, etc but encapsulating
+ // them in an opaque unsafe coercion to Object.
+ return new JUnsafeTypeCoercion(original.getSourceInfo(), javaLangObject, original);
+ }
+
+ ClassScope scope = curClass.scope;
+ JMethod boxingMethod = typeMap.get(JdtUtil.getBoxingMethodBinding(scope, primitiveType));
+ return new JMethodCall(original.getSourceInfo(), null, boxingMethod, original);
+ }
+
+ private JExpression unbox(JExpression original, BaseTypeBinding primitiveType) {
+ return unbox(original, primitiveType, true);
+ }
+
+ private JExpression unbox(
+ JExpression original, BaseTypeBinding primitiveType, boolean needsExplicitCast) {
+
+ ClassScope scope = curClass.scope;
+ if (needsExplicitCast) {
+ // Direct cast from non-boxed-type reference type to a primitive type,
+ // wrap with a cast operation of the (boxed) expected type.
+ JReferenceType boxedType =
+ (JReferenceType) typeMap.get(JdtUtil.getBoxedTypeBinding(scope, primitiveType));
+ original =
+ new JCastOperation(original.getSourceInfo(), boxedType, original);
+ }
+ JMethod unboxingMethod = typeMap.get(JdtUtil.getUnboxingMethodBinding(scope, primitiveType));
+ return new JMethodCall(original.getSourceInfo(), original, unboxingMethod);
}
private void createBridgeMethod(SyntheticMethodBinding jdtBridgeMethod) {
@@ -3063,12 +3085,18 @@
}
private JExpression maybeBoxOrUnbox(JExpression original, int implicitConversion) {
- if (implicitConversion != -1) {
- if ((implicitConversion & TypeIds.BOXING) != 0) {
- return box(original, implicitConversion);
- } else if ((implicitConversion & TypeIds.UNBOXING) != 0) {
- return unbox(original, implicitConversion);
- }
+ return maybeBoxOrUnbox(original, implicitConversion, false);
+ }
+
+ private JExpression maybeBoxOrUnbox(
+ JExpression original, int implicitConversion, boolean doNotAutobox) {
+ if (JdtUtil.requiresBoxing(implicitConversion)) {
+ return box(original, JdtUtil.getBoxingPrimitiveType(curClass.scope, implicitConversion),
+ doNotAutobox);
+ }
+ if (JdtUtil.requiresUnboxing(implicitConversion)) {
+ return unbox(original, JdtUtil.getUnboxingPrimitiveType(curClass.scope, implicitConversion),
+ JdtUtil.needsCastBeforeUnbox(curClass.scope, implicitConversion));
}
return original;
}
@@ -3088,7 +3116,7 @@
return expression;
}
- private JExpression maybeInsertUnsafeTypeCoersion(JType expected, JExpression expression) {
+ private JExpression maybeInsertUnsafeTypeCoercion(JType expected, JExpression expression) {
if (expected != expression.getType()) {
// A generic call marked as @UncheckedCast.
return new JUnsafeTypeCoercion(expression.getSourceInfo(), expected, expression);
@@ -3101,25 +3129,33 @@
return nodeStack.remove(nodeStack.size() - 1);
}
- private List<JExpression> popCallArgs(SourceInfo info, Expression[] jdtArgs,
- MethodBinding binding) {
- List<JExpression> args = pop(jdtArgs);
- if (!binding.isVarargs()) {
+ private List<JExpression> popCallArguments(SourceInfo info, Expression[] arguments,
+ MethodBinding methodBinding) {
+ List<JExpression> args = pop(arguments);
+ for (int i = 0; i < args.size(); i++) {
+ // Account for varargs parameter.
+ int parameterIndex = Math.min(i, methodBinding.parameters.length - 1);
+ args.set(i, maybeBoxOrUnbox(
+ args.get(i),
+ arguments[i].implicitConversion,
+ isDoNotAutoBoxParameter(methodBinding, parameterIndex)));
+ }
+ if (!methodBinding.isVarargs()) {
return args;
}
// Handle the odd var-arg case.
- if (jdtArgs == null) {
+ if (arguments == null) {
// Get writable collection (args is currently Collections.emptyList()).
args = Lists.newArrayListWithCapacity(1);
}
- TypeBinding[] params = binding.parameters;
+ TypeBinding[] params = methodBinding.parameters;
int varArg = params.length - 1;
// See if there's a single varArg which is already an array.
if (args.size() == params.length) {
- if (jdtArgs[varArg].resolvedType.isCompatibleWith(params[varArg])) {
+ if (arguments[varArg].resolvedType.isCompatibleWith(params[varArg])) {
// Already the correct array type.
return args;
}
@@ -3135,6 +3171,15 @@
return args;
}
+ private boolean isDoNotAutoBoxParameter(MethodBinding methodBinding, int parameterIndex) {
+ AnnotationBinding[][] parameterAnnotations = methodBinding.getParameterAnnotations();
+ return parameterAnnotations != null
+ && parameterAnnotations.length > parameterIndex
+ && parameterAnnotations[parameterIndex] != null
+ && JdtUtil.getAnnotationByName(parameterAnnotations[parameterIndex],
+ "javaemul.internal.annotations.DoNotAutobox") != null;
+ }
+
private List<? extends JNode> popList(int count) {
List<JNode> tail = nodeStack.subList(nodeStack.size() - count, nodeStack.size());
// Make a copy.
@@ -3455,7 +3500,7 @@
return result;
}
- private JExpression simplify(JExpression result, Expression x) {
+ private JExpression maybeBoxOrUnbox(JExpression result, Expression x) {
return maybeBoxOrUnbox(result, x.implicitConversion);
}
@@ -3467,34 +3512,6 @@
return expression;
}
- private JExpression unbox(JExpression original, int implicitConversion) {
- int compileTypeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
- ClassScope scope = curClass.scope;
- TypeBinding targetBinding = TypeBinding.wellKnownType(scope, compileTypeId);
- if (!(targetBinding instanceof BaseTypeBinding)) {
- // Direct cast from non-boxed-type reference type to a primitive type,
- // wrap with a cast operation of the (boxed) expected type.
- int runtimeTypeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
- TypeBinding runtimeTypeBinding = TypeBinding.wellKnownType(scope, runtimeTypeId);
- ReferenceBinding boxType = (ReferenceBinding) scope.boxing(runtimeTypeBinding);
- original =
- new JCastOperation(original.getSourceInfo(), typeMap.get(boxType), original);
- targetBinding = runtimeTypeBinding;
- assert (targetBinding instanceof BaseTypeBinding);
- }
-
- BaseTypeBinding primitiveType = (BaseTypeBinding) targetBinding;
-
- ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
- char[] selector = CharOperation.concat(primitiveType.simpleName, VALUE_SUFFIX_);
- MethodBinding valueMethod =
- boxType.getExactMethod(selector, NO_TYPES, scope.compilationUnitScope());
- assert valueMethod != null;
- JMethod unboxMethod = typeMap.get(valueMethod);
- JMethodCall call = new JMethodCall(original.getSourceInfo(), original, unboxMethod);
- return call;
- }
-
private void writeEnumValueOfMethod(JEnumType type, JMethod method, JMethod valuesMethod) {
JField mapField;
TypeBinding mapType;
@@ -3732,10 +3749,8 @@
private static final String CREATE_VALUE_OF_MAP_METHOD_NAME = "createValueOfMap";
private static final String LENGTH_FIELD_NAME = "length";
- private static final String VALUE_SUFFIX = "Value";
private static final char[] CREATE_VALUE_OF_MAP_ = CREATE_VALUE_OF_MAP_METHOD_NAME.toCharArray();
- private static final char[] VALUE_SUFFIX_ = VALUE_SUFFIX.toCharArray();
private static final char[] VALUE_OF_ = VALUE_OF_METHOD_NAME.toCharArray();
private static final char[] VALUES_ = VALUES_METHOD_NAME.toCharArray();
private static final char[] ORDINAL_ = ORDINAL_METHOD_NAME.toCharArray();
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/annotations/DoNotAutobox.java b/user/super/com/google/gwt/emul/javaemul/internal/annotations/DoNotAutobox.java
new file mode 100644
index 0000000..479548f
--- /dev/null
+++ b/user/super/com/google/gwt/emul/javaemul/internal/annotations/DoNotAutobox.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 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 javaemul.internal.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to prevent autoboxing of arguments on method calls.
+ */
+@Target(ElementType.PARAMETER)
+public @interface DoNotAutobox {
+}
diff --git a/user/test/com/google/gwt/core/interop/JsMethodTest.java b/user/test/com/google/gwt/core/interop/JsMethodTest.java
index dd3124d..828e016 100644
--- a/user/test/com/google/gwt/core/interop/JsMethodTest.java
+++ b/user/test/com/google/gwt/core/interop/JsMethodTest.java
@@ -19,6 +19,7 @@
import com.google.gwt.junit.client.GWTTestCase;
+import javaemul.internal.annotations.DoNotAutobox;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsProperty;
@@ -80,4 +81,36 @@
setJsInteropSecret("very secret!");
assertEquals("very secret!", getJsInteropSecret());
}
+
+ public static double returnWithoutBoxing(@DoNotAutobox Object object) {
+ return (Double) object;
+ }
+
+ public static double sumWithoutBoxing(@DoNotAutobox Object... objects) {
+ double sum = 0;
+ for (Object o : objects) {
+ sum += (Double) o;
+ }
+ return sum;
+ }
+
+ @JsMethod
+ public static double sumWithoutBoxingJsVarargs(@DoNotAutobox Object... objects) {
+ double sum = 0;
+ for (Object o : objects) {
+ sum += (Double) o;
+ }
+ return sum;
+ }
+
+ public void testDoNotAutobox() {
+ assertEquals(3.0, returnWithoutBoxing(3));
+ assertEquals(4.5, sumWithoutBoxing(1, 1.5, (short) 1, (byte) 1));
+ assertEquals(4.5, sumWithoutBoxingJsVarargs(1, 1.5, (short) 1, (byte) 1));
+ try {
+ returnWithoutBoxing(Long.MAX_VALUE);
+ fail("Should have thrown ClassCastException");
+ } catch (ClassCastException expected) {
+ }
+ }
}