All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.ComputeCastabilityInformation Maven / Gradle / Ivy

/*
 * 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.dev.jjs.impl;

import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.HasName;
import com.google.gwt.dev.jjs.ast.JArrayRef;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JCastMap;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JTypeOracle;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * Builds minimal cast maps to cover cast and instanceof operations. Depends
 * on {@link CatchBlockNormalizer}, {@link CompoundAssignmentNormalizer},
 * {@link Devirtualizer}, and {@link LongCastNormalizer} having already run.
 * 

* May or may not include trivial casts depending on configuration. */ public class ComputeCastabilityInformation { private class AssignTypeCastabilityVisitor extends JVisitor { private final Set alreadyRan = Sets.newHashSet(); private final Map castableTypesMap = Maps.newIdentityHashMap(); private final List instantiatedArrayTypes = Lists.newArrayList(); private final Multimap castSourceTypesPerCastTargetType = HashMultimap.create(); { for (JArrayType arrayType : program.getAllArrayTypes()) { if (typeOracle.isInstantiatedType(arrayType)) { instantiatedArrayTypes.add(arrayType); } } // Force entries for Object and String. recordCastInternal(program.getTypeJavaLangObject(), program.getTypeJavaLangObject()); recordCastInternal(program.getTypeJavaLangString(), program.getTypeJavaLangObject()); // Force entries for interfaces implemented by String recordCastInternal(program.getIndexedType("Serializable"), program.getTypeJavaLangObject()); recordCastInternal(program.getIndexedType("CharSequence"), program.getTypeJavaLangObject()); recordCastInternal(program.getIndexedType("Comparable"), program.getTypeJavaLangObject()); } public void computeTypeCastabilityMaps() { // do String first (which will pull in Object also, it's superclass). computeCastMap(program.getTypeJavaLangString()); assert (castableTypesMap.size() == 2); /* * Compute the list of classes than can successfully satisfy cast * requests, along with the set of types they can be successfully cast to. * Do it in super type order. */ for (JReferenceType type : program.getDeclaredTypes()) { if (type instanceof JClassType) { computeCastMap(type); } } for (JArrayType type : instantiatedArrayTypes) { computeCastMap(type); } // pass our info to JProgram program.initTypeInfo(castableTypesMap); } /* * If this expression could possibly generate an ArrayStoreException, we * must record a query on the element type being assigned to. */ @Override public void endVisit(JBinaryOperation x, Context ctx) { if (x.getOp().isAssignment() && x.getLhs() instanceof JArrayRef) { // first, calculate the transitive closure of all possible runtime types // the lhs could be JArrayRef lhsArrayRef = (JArrayRef) x.getLhs(); JType elementType = lhsArrayRef.getType(); if (elementType instanceof JNullType) { // will generate a null pointer exception instead return; } // primitives are statically correct if (!(elementType instanceof JReferenceType)) { return; } // element type being final means the assignment is statically correct if (elementType.isFinal()) { return; } /* * For every instantiated array type that could -in theory- be the * runtime type of the lhs, we must record a cast from the rhs to the * prospective element type of the lhs. */ JType rhsType = x.getRhs().getType(); assert (rhsType instanceof JReferenceType); JArrayType lhsArrayType = lhsArrayRef.getArrayType(); for (JArrayType arrayType : instantiatedArrayTypes) { if (typeOracle.canTheoreticallyCast(arrayType, lhsArrayType)) { JType itElementType = arrayType.getElementType(); if (itElementType instanceof JReferenceType) { recordCast(itElementType, x.getRhs()); } } } } } @Override public void endVisit(JCastOperation x, Context ctx) { if (disableCastChecking || x.getCastType() == program.getTypeNull()) { return; } recordCast(x.getCastType(), x.getExpr()); } @Override public void endVisit(JInstanceOf x, Context ctx) { assert (x.getTestType() != program.getTypeNull()); recordCast(x.getTestType(), x.getExpr()); } private boolean canTriviallyCastJsoSemantics(JReferenceType type, JReferenceType qType) { type = type.getUnderlyingType(); qType = qType.getUnderlyingType(); if (typeOracle.canTriviallyCast(type, qType)) { return true; } if (type instanceof JArrayType && qType instanceof JArrayType) { JArrayType aType = (JArrayType) type; JArrayType aqType = (JArrayType) qType; return (typeOracle.isJavaScriptObject(aType.getLeafType()) && typeOracle.isJavaScriptObject(aqType.getLeafType())); } return (typeOracle.isJavaScriptObject(type) && typeOracle.isJavaScriptObject(qType)); } /** * Create the mapping from a class to the types it can be cast to. */ private void computeCastMap(JReferenceType type) { if (type == null || alreadyRan.contains(type)) { return; } assert (type == type.getUnderlyingType()); alreadyRan.add(type); // Visit super type. if (type instanceof JClassType) { computeCastMap(((JClassType) type).getSuperClass()); } if (!typeOracle.isInstantiatedType(type) || typeOracle.isJavaScriptObject(type)) { return; } // Find all possible query types which I can satisfy Set castableTypes = new TreeSet(HasName.BY_NAME_COMPARATOR); /* * NOTE: non-deterministic iteration over HashSet and HashMap. Okay * because we're sorting the results. */ for (JReferenceType castTargetType : castSourceTypesPerCastTargetType.keySet()) { if (!canTriviallyCastJsoSemantics(type, castTargetType)) { continue; } Collection castSourceTypes = castSourceTypesPerCastTargetType.get(castTargetType); /** * Handles JSO[] -> JSO[] case now that canCastTrivially doesn't deal * with JSO cross-casts anymore. */ for (JReferenceType castSourceType : castSourceTypes) { if (canTriviallyCastJsoSemantics(type, castSourceType) || typeOracle.isJavaScriptObject(castTargetType)) { boolean isTrivialCast = castTargetType == program.getTypeJavaLangObject() || castTargetType == program.getJavaScriptObject(); if (recordTrivialCasts || !isTrivialCast) { castableTypes.add(castTargetType); } break; } } } /* * Don't add an entry for empty answer sets, except for Object and String * which require entries. */ if (castableTypes.isEmpty() && type != program.getTypeJavaLangObject() && type != program.getTypeJavaLangString()) { return; } // add an entry for me castableTypesMap.put(type, new JCastMap(SourceOrigin.UNKNOWN, program.getTypeJavaLangObject(), Collections.unmodifiableSet(castableTypes))); } private void recordCast(JType targetType, JExpression rhs) { if (!(targetType instanceof JReferenceType)) { return; } targetType = targetType.getUnderlyingType(); assert rhs.getType() instanceof JReferenceType; JReferenceType rhsType = (JReferenceType) rhs.getType().getUnderlyingType(); if (!recordTrivialCasts && typeOracle.canTriviallyCast(rhsType, (JReferenceType) targetType)) { // don't record a type for trivial casts that won't generate code return; } if (!recordTrivialCasts && typeOracle.isJavaScriptObject(targetType)) { // If the target type is a JavaScriptObject, don't record an id. return; } recordCastInternal((JReferenceType) targetType, rhsType); } private void recordCastInternal(JReferenceType toType, JReferenceType rhsType) { toType = toType.getUnderlyingType(); rhsType = rhsType.getUnderlyingType(); if (toType instanceof JArrayType) { // Arrays of any subclass of JavaScriptObject are considered arrays of JavaScriptObject // for casting and instanceof purposes. toType = (JReferenceType) program.normalizeJsoType(toType); } castSourceTypesPerCastTargetType.put(toType, rhsType); } } public static void exec(JProgram program, boolean disableCastChecking, boolean recordTrivialCasts) { new ComputeCastabilityInformation(program, disableCastChecking, recordTrivialCasts).execImpl(); } public static void exec(JProgram program, boolean disableCastChecking) { new ComputeCastabilityInformation(program, disableCastChecking, false).execImpl(); } private final boolean disableCastChecking; private final boolean recordTrivialCasts; private final JProgram program; private final JTypeOracle typeOracle; private ComputeCastabilityInformation(JProgram program, boolean disableCastChecking, boolean recordTrivialCasts) { this.program = program; this.typeOracle = program.typeOracle; this.disableCastChecking = disableCastChecking; this.recordTrivialCasts = recordTrivialCasts; } private void execImpl() { AssignTypeCastabilityVisitor assigner = new AssignTypeCastabilityVisitor(); assigner.accept(program); assigner.computeTypeCastabilityMaps(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy