Home | History | Annotate | Download | only in types
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include "scoped_thread_state_change.h"
     17 #include "sea_ir/types/type_inference.h"
     18 #include "sea_ir/types/type_inference_visitor.h"
     19 #include "sea_ir/ir/sea.h"
     20 
     21 namespace sea_ir {
     22 
     23 bool TypeInference::IsPrimitiveDescriptor(char descriptor) {
     24   switch (descriptor) {
     25   case 'I':
     26   case 'C':
     27   case 'S':
     28   case 'B':
     29   case 'Z':
     30   case 'F':
     31   case 'D':
     32   case 'J':
     33     return true;
     34   default:
     35     return false;
     36   }
     37 }
     38 
     39 FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types)
     40     : dex_file_(graph->GetDexFile()), dex_method_idx_(graph->method_idx_), type_cache_(types),
     41     method_access_flags_(graph->method_access_flags_) {
     42   const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
     43   const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
     44   declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
     45 }
     46 
     47 FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, InstructionNode* inst,
     48     art::verifier::RegTypeCache* types): dex_file_(graph->GetDexFile()),
     49         dex_method_idx_(inst->GetInstruction()->VRegB_35c()), type_cache_(types),
     50         method_access_flags_(0) {
     51   // TODO: Test that GetDeclaredArgumentTypes() works correctly when using this constructor.
     52   const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
     53   const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
     54   declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
     55 }
     56 
     57 const Type* FunctionTypeInfo::GetReturnValueType() {
     58   const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
     59   uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
     60   const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
     61   art::ScopedObjectAccess soa(art::Thread::Current());
     62   const Type& return_type = type_cache_->FromDescriptor(NULL, descriptor, false);
     63   return &return_type;
     64 }
     65 
     66 
     67 
     68 std::vector<const Type*> FunctionTypeInfo::GetDeclaredArgumentTypes() {
     69   art::ScopedObjectAccess soa(art::Thread::Current());
     70   std::vector<const Type*> argument_types;
     71   // TODO: The additional (fake) Method parameter is added on the first position,
     72   //       but is represented as integer because we don't support  pointers yet.
     73   argument_types.push_back(&(type_cache_->Integer()));
     74   // Include the "this" pointer.
     75   size_t cur_arg = 0;
     76   if (!IsStatic()) {
     77     // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
     78     // argument as uninitialized. This restricts field access until the superclass constructor is
     79     // called.
     80     const art::verifier::RegType& declaring_class = GetDeclaringClass();
     81     if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
     82       argument_types.push_back(&(type_cache_->UninitializedThisArgument(declaring_class)));
     83     } else {
     84       argument_types.push_back(&declaring_class);
     85     }
     86     cur_arg++;
     87   }
     88   // Include the types of the parameters in the Java method signature.
     89   const art::DexFile::ProtoId& proto_id =
     90       dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
     91   art::DexFileParameterIterator iterator(*dex_file_, proto_id);
     92 
     93   for (; iterator.HasNext(); iterator.Next()) {
     94     const char* descriptor = iterator.GetDescriptor();
     95     if (descriptor == NULL) {
     96       LOG(FATAL) << "Error: Encountered null type descriptor for function argument.";
     97     }
     98     switch (descriptor[0]) {
     99       case 'L':
    100       case '[':
    101         // We assume that reference arguments are initialized. The only way it could be otherwise
    102         // (assuming the caller was verified) is if the current method is <init>, but in that case
    103         // it's effectively considered initialized the instant we reach here (in the sense that we
    104         // can return without doing anything or call virtual methods).
    105         {
    106           const Type& reg_type = type_cache_->FromDescriptor(NULL, descriptor, false);
    107           argument_types.push_back(&reg_type);
    108         }
    109         break;
    110       case 'Z':
    111         argument_types.push_back(&type_cache_->Boolean());
    112         break;
    113       case 'C':
    114         argument_types.push_back(&type_cache_->Char());
    115         break;
    116       case 'B':
    117         argument_types.push_back(&type_cache_->Byte());
    118         break;
    119       case 'I':
    120         argument_types.push_back(&type_cache_->Integer());
    121         break;
    122       case 'S':
    123         argument_types.push_back(&type_cache_->Short());
    124         break;
    125       case 'F':
    126         argument_types.push_back(&type_cache_->Float());
    127         break;
    128       case 'J':
    129       case 'D': {
    130         // TODO: Figure out strategy for two-register operands (double, long)
    131         LOG(FATAL) << "Error: Type inference for 64-bit variables has not been implemented.";
    132         break;
    133       }
    134       default:
    135         LOG(FATAL) << "Error: Unexpected signature encountered during type inference.";
    136     }
    137     cur_arg++;
    138   }
    139   return argument_types;
    140 }
    141 
    142 // TODO: Lock is only used for dumping types (during development). Remove this for performance.
    143 void TypeInference::ComputeTypes(SeaGraph* graph) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
    144   std::vector<Region*>* regions = graph->GetRegions();
    145   std::list<InstructionNode*> worklist;
    146   // Fill the work-list with all instructions.
    147   for (std::vector<Region*>::const_iterator region_it = regions->begin();
    148       region_it != regions->end(); region_it++) {
    149     std::vector<PhiInstructionNode*>* phi_instructions = (*region_it)->GetPhiNodes();
    150     std::copy(phi_instructions->begin(), phi_instructions->end(), std::back_inserter(worklist));
    151     std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
    152     std::copy(instructions->begin(), instructions->end(), std::back_inserter(worklist));
    153   }
    154   TypeInferenceVisitor tiv(graph, &type_data_, type_cache_);
    155   // Record return type of the function.
    156   graph->Accept(&tiv);
    157   const Type* new_type = tiv.GetType();
    158   type_data_.SetTypeOf(-1, new_type);   // TODO: Record this info in a way that
    159                                         //      does not need magic constants.
    160                                         //      Make SeaGraph a SeaNode?
    161 
    162   // Sparse (SSA) fixed-point algorithm that processes each instruction in the work-list,
    163   // adding consumers of instructions whose result changed type back into the work-list.
    164   // Note: According to [1] list iterators should not be invalidated on insertion,
    165   //       which simplifies the implementation; not 100% sure other STL implementations
    166   //       maintain this invariant, but they should.
    167   //       [1] http://www.sgi.com/tech/stl/List.html
    168   // TODO: Making this conditional (as in sparse conditional constant propagation) would be good.
    169   // TODO: Remove elements as I go.
    170   for (std::list<InstructionNode*>::const_iterator instruction_it = worklist.begin();
    171         instruction_it != worklist.end(); instruction_it++) {
    172     (*instruction_it)->Accept(&tiv);
    173     const Type* old_type = type_data_.FindTypeOf((*instruction_it)->Id());
    174     const Type* new_type = tiv.GetType();
    175     bool type_changed = (old_type != new_type);
    176     if (type_changed) {
    177       type_data_.SetTypeOf((*instruction_it)->Id(), new_type);
    178       // Add SSA consumers of the current instruction to the work-list.
    179       std::vector<InstructionNode*>* consumers = (*instruction_it)->GetSSAConsumers();
    180       for (std::vector<InstructionNode*>::iterator consumer = consumers->begin();
    181           consumer != consumers->end(); consumer++) {
    182         worklist.push_back(*consumer);
    183       }
    184     }
    185   }
    186 }
    187 }   // namespace sea_ir
    188