| OLD | NEW | 
|    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |    2 // for details. All rights reserved. Use of this source code is governed by a | 
|    3 // BSD-style license that can be found in the LICENSE file. |    3 // BSD-style license that can be found in the LICENSE file. | 
|    4  |    4  | 
|    5 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |    5 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; | 
|    6 import '../compiler.dart' show Compiler; |    6 import '../compiler.dart' show Compiler; | 
|    7 import '../constants/constant_system.dart'; |    7 import '../constants/constant_system.dart'; | 
|    8 import '../constants/values.dart'; |    8 import '../constants/values.dart'; | 
|    9 import '../elements/elements.dart'; |    9 import '../elements/elements.dart'; | 
|   10 import '../js_backend/backend_helpers.dart' show BackendHelpers; |   10 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   89     // TODO(sra): Also do self-interceptor rewrites on a per-use basis. |   89     // TODO(sra): Also do self-interceptor rewrites on a per-use basis. | 
|   90  |   90  | 
|   91     HInstruction constant = tryComputeConstantInterceptor( |   91     HInstruction constant = tryComputeConstantInterceptor( | 
|   92         invoke.inputs[1], interceptor.interceptedClasses); |   92         invoke.inputs[1], interceptor.interceptedClasses); | 
|   93     if (constant != null) { |   93     if (constant != null) { | 
|   94       invoke.changeUse(interceptor, constant); |   94       invoke.changeUse(interceptor, constant); | 
|   95     } |   95     } | 
|   96     return false; |   96     return false; | 
|   97   } |   97   } | 
|   98  |   98  | 
|   99   bool canUseSelfForInterceptor(HInstruction receiver, |   99   bool canUseSelfForInterceptor( | 
|  100                                 Set<ClassElement> interceptedClasses) { |  100       HInstruction receiver, Set<ClassElement> interceptedClasses) { | 
|  101  |  | 
|  102     if (receiver.canBePrimitive(compiler)) { |  101     if (receiver.canBePrimitive(compiler)) { | 
|  103       // Primitives always need interceptors. |  102       // Primitives always need interceptors. | 
|  104       return false; |  103       return false; | 
|  105     } |  104     } | 
|  106     if (receiver.canBeNull() && |  105     if (receiver.canBeNull() && | 
|  107         interceptedClasses.contains(helpers.jsNullClass)) { |  106         interceptedClasses.contains(helpers.jsNullClass)) { | 
|  108       // Need the JSNull interceptor. |  107       // Need the JSNull interceptor. | 
|  109       return false; |  108       return false; | 
|  110     } |  109     } | 
|  111  |  110  | 
|  112     // All intercepted classes extend `Interceptor`, so if the receiver can't be |  111     // All intercepted classes extend `Interceptor`, so if the receiver can't be | 
|  113     // a class extending `Interceptor` then it can be called directly. |  112     // a class extending `Interceptor` then it can be called directly. | 
|  114     return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, classWorld) |  113     return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, classWorld) | 
|  115         .isDisjoint(receiver.instructionType, classWorld); |  114         .isDisjoint(receiver.instructionType, classWorld); | 
|  116   } |  115   } | 
|  117  |  116  | 
|  118   HInstruction tryComputeConstantInterceptor( |  117   HInstruction tryComputeConstantInterceptor( | 
|  119       HInstruction input, |  118       HInstruction input, Set<ClassElement> interceptedClasses) { | 
|  120       Set<ClassElement> interceptedClasses) { |  | 
|  121     if (input == graph.explicitReceiverParameter) { |  119     if (input == graph.explicitReceiverParameter) { | 
|  122       // If `explicitReceiverParameter` is set it means the current method is an |  120       // If `explicitReceiverParameter` is set it means the current method is an | 
|  123       // interceptor method, and `this` is the interceptor.  The caller just did |  121       // interceptor method, and `this` is the interceptor.  The caller just did | 
|  124       // `getInterceptor(foo).currentMethod(foo)` to enter the current method. |  122       // `getInterceptor(foo).currentMethod(foo)` to enter the current method. | 
|  125       return graph.thisInstruction; |  123       return graph.thisInstruction; | 
|  126     } |  124     } | 
|  127  |  125  | 
|  128     ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( |  126     ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( | 
|  129         input.instructionType, interceptedClasses); |  127         input.instructionType, interceptedClasses); | 
|  130  |  128  | 
|  131     if (constantInterceptor == null) return null; |  129     if (constantInterceptor == null) return null; | 
|  132  |  130  | 
|  133     // If we just happen to be in an instance method of the constant |  131     // If we just happen to be in an instance method of the constant | 
|  134     // interceptor, `this` is a shorter alias. |  132     // interceptor, `this` is a shorter alias. | 
|  135     if (constantInterceptor == work.element.enclosingClass && |  133     if (constantInterceptor == work.element.enclosingClass && | 
|  136         graph.thisInstruction != null) { |  134         graph.thisInstruction != null) { | 
|  137       return graph.thisInstruction; |  135       return graph.thisInstruction; | 
|  138     } |  136     } | 
|  139  |  137  | 
|  140     ConstantValue constant = |  138     ConstantValue constant = | 
|  141         new InterceptorConstantValue(constantInterceptor.thisType); |  139         new InterceptorConstantValue(constantInterceptor.thisType); | 
|  142     return graph.addConstant(constant, compiler); |  140     return graph.addConstant(constant, compiler); | 
|  143   } |  141   } | 
|  144  |  142  | 
|  145   ClassElement tryComputeConstantInterceptorFromType( |  143   ClassElement tryComputeConstantInterceptorFromType( | 
|  146       TypeMask type, |  144       TypeMask type, Set<ClassElement> interceptedClasses) { | 
|  147       Set<ClassElement> interceptedClasses) { |  | 
|  148  |  | 
|  149     if (type.isNullable) { |  145     if (type.isNullable) { | 
|  150       if (type.isNull) { |  146       if (type.isNull) { | 
|  151         return helpers.jsNullClass; |  147         return helpers.jsNullClass; | 
|  152       } |  148       } | 
|  153     } else if (type.containsOnlyInt(classWorld)) { |  149     } else if (type.containsOnlyInt(classWorld)) { | 
|  154       return helpers.jsIntClass; |  150       return helpers.jsIntClass; | 
|  155     } else if (type.containsOnlyDouble(classWorld)) { |  151     } else if (type.containsOnlyDouble(classWorld)) { | 
|  156       return helpers.jsDoubleClass; |  152       return helpers.jsDoubleClass; | 
|  157     } else if (type.containsOnlyBool(classWorld)) { |  153     } else if (type.containsOnlyBool(classWorld)) { | 
|  158       return helpers.jsBoolClass; |  154       return helpers.jsBoolClass; | 
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  223  |  219  | 
|  224     Set<ClassElement> interceptedClasses; |  220     Set<ClassElement> interceptedClasses; | 
|  225     HInstruction dominator = findDominator(node.usedBy); |  221     HInstruction dominator = findDominator(node.usedBy); | 
|  226     // If there is a call that dominates all other uses, we can use just the |  222     // If there is a call that dominates all other uses, we can use just the | 
|  227     // selector of that instruction. |  223     // selector of that instruction. | 
|  228     if (dominator is HInvokeDynamic && |  224     if (dominator is HInvokeDynamic && | 
|  229         dominator.isCallOnInterceptor(compiler) && |  225         dominator.isCallOnInterceptor(compiler) && | 
|  230         node == dominator.receiver && |  226         node == dominator.receiver && | 
|  231         useCount(dominator, node) == 1) { |  227         useCount(dominator, node) == 1) { | 
|  232       interceptedClasses = |  228       interceptedClasses = | 
|  233             backend.getInterceptedClassesOn(dominator.selector.name); |  229           backend.getInterceptedClassesOn(dominator.selector.name); | 
|  234  |  230  | 
|  235       // If we found that we need number, we must still go through all |  231       // If we found that we need number, we must still go through all | 
|  236       // uses to check if they require int, or double. |  232       // uses to check if they require int, or double. | 
|  237       if (interceptedClasses.contains(helpers.jsNumberClass) && |  233       if (interceptedClasses.contains(helpers.jsNumberClass) && | 
|  238           !(interceptedClasses.contains(helpers.jsDoubleClass) || |  234           !(interceptedClasses.contains(helpers.jsDoubleClass) || | 
|  239             interceptedClasses.contains(helpers.jsIntClass))) { |  235               interceptedClasses.contains(helpers.jsIntClass))) { | 
|  240         for (HInstruction user in node.usedBy) { |  236         for (HInstruction user in node.usedBy) { | 
|  241           if (user is! HInvoke) continue; |  237           if (user is! HInvoke) continue; | 
|  242           Set<ClassElement> intercepted = |  238           Set<ClassElement> intercepted = | 
|  243               backend.getInterceptedClassesOn(user.selector.name); |  239               backend.getInterceptedClassesOn(user.selector.name); | 
|  244           if (intercepted.contains(helpers.jsIntClass)) { |  240           if (intercepted.contains(helpers.jsIntClass)) { | 
|  245             interceptedClasses.add(helpers.jsIntClass); |  241             interceptedClasses.add(helpers.jsIntClass); | 
|  246           } |  242           } | 
|  247           if (intercepted.contains(helpers.jsDoubleClass)) { |  243           if (intercepted.contains(helpers.jsDoubleClass)) { | 
|  248             interceptedClasses.add(helpers.jsDoubleClass); |  244             interceptedClasses.add(helpers.jsDoubleClass); | 
|  249           } |  245           } | 
|  250         } |  246         } | 
|  251       } |  247       } | 
|  252     } else { |  248     } else { | 
|  253       interceptedClasses = new Set<ClassElement>(); |  249       interceptedClasses = new Set<ClassElement>(); | 
|  254       for (HInstruction user in node.usedBy) { |  250       for (HInstruction user in node.usedBy) { | 
|  255         if (user is HInvokeDynamic && |  251         if (user is HInvokeDynamic && | 
|  256             user.isCallOnInterceptor(compiler) && |  252             user.isCallOnInterceptor(compiler) && | 
|  257             node == user.receiver && |  253             node == user.receiver && | 
|  258             useCount(user, node) == 1) { |  254             useCount(user, node) == 1) { | 
|  259           interceptedClasses.addAll( |  255           interceptedClasses | 
|  260               backend.getInterceptedClassesOn(user.selector.name)); |  256               .addAll(backend.getInterceptedClassesOn(user.selector.name)); | 
|  261         } else if (user is HInvokeSuper && |  257         } else if (user is HInvokeSuper && | 
|  262                    user.isCallOnInterceptor(compiler) && |  258             user.isCallOnInterceptor(compiler) && | 
|  263                    node == user.receiver && |  259             node == user.receiver && | 
|  264                    useCount(user, node) == 1) { |  260             useCount(user, node) == 1) { | 
|  265           interceptedClasses.addAll( |  261           interceptedClasses | 
|  266               backend.getInterceptedClassesOn(user.selector.name)); |  262               .addAll(backend.getInterceptedClassesOn(user.selector.name)); | 
|  267         } else { |  263         } else { | 
|  268           // Use a most general interceptor for other instructions, example, |  264           // Use a most general interceptor for other instructions, example, | 
|  269           // is-checks and escaping interceptors. |  265           // is-checks and escaping interceptors. | 
|  270           interceptedClasses.addAll(backend.interceptedClasses); |  266           interceptedClasses.addAll(backend.interceptedClasses); | 
|  271           break; |  267           break; | 
|  272         } |  268         } | 
|  273       } |  269       } | 
|  274     } |  270     } | 
|  275  |  271  | 
|  276     node.interceptedClasses = interceptedClasses; |  272     node.interceptedClasses = interceptedClasses; | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
|  304     // interceptor. |  300     // interceptor. | 
|  305     if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { |  301     if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { | 
|  306       if (!interceptedClasses.contains(helpers.jsNullClass)) { |  302       if (!interceptedClasses.contains(helpers.jsNullClass)) { | 
|  307         // Can use `(receiver && C)` only if receiver is either null or truthy. |  303         // Can use `(receiver && C)` only if receiver is either null or truthy. | 
|  308         if (!(receiver.canBePrimitiveNumber(compiler) || |  304         if (!(receiver.canBePrimitiveNumber(compiler) || | 
|  309             receiver.canBePrimitiveBoolean(compiler) || |  305             receiver.canBePrimitiveBoolean(compiler) || | 
|  310             receiver.canBePrimitiveString(compiler))) { |  306             receiver.canBePrimitiveString(compiler))) { | 
|  311           ClassElement interceptorClass = tryComputeConstantInterceptorFromType( |  307           ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | 
|  312               receiver.instructionType.nonNullable(), interceptedClasses); |  308               receiver.instructionType.nonNullable(), interceptedClasses); | 
|  313           if (interceptorClass != null) { |  309           if (interceptorClass != null) { | 
|  314             HInstruction constantInstruction = |  310             HInstruction constantInstruction = graph.addConstant( | 
|  315                 graph.addConstant( |  311                 new InterceptorConstantValue(interceptorClass.thisType), | 
|  316                     new InterceptorConstantValue(interceptorClass.thisType), |  312                 compiler); | 
|  317                     compiler); |  | 
|  318             node.conditionalConstantInterceptor = constantInstruction; |  313             node.conditionalConstantInterceptor = constantInstruction; | 
|  319             constantInstruction.usedBy.add(node); |  314             constantInstruction.usedBy.add(node); | 
|  320             return false; |  315             return false; | 
|  321           } |  316           } | 
|  322         } |  317         } | 
|  323       } |  318       } | 
|  324     } |  319     } | 
|  325  |  320  | 
|  326     // Try creating a one-shot interceptor or optimized is-check |  321     // Try creating a one-shot interceptor or optimized is-check | 
|  327     if (compiler.options.hasIncrementalSupport) return false; |  322     if (compiler.options.hasIncrementalSupport) return false; | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
|  351           return replaceUserWith(instanceofCheck); |  346           return replaceUserWith(instanceofCheck); | 
|  352         } |  347         } | 
|  353       } |  348       } | 
|  354     } else if (user is HInvokeDynamic) { |  349     } else if (user is HInvokeDynamic) { | 
|  355       if (node == user.inputs[0]) { |  350       if (node == user.inputs[0]) { | 
|  356         // Replace the user with a [HOneShotInterceptor]. |  351         // Replace the user with a [HOneShotInterceptor]. | 
|  357         HConstant nullConstant = graph.addConstantNull(compiler); |  352         HConstant nullConstant = graph.addConstantNull(compiler); | 
|  358         List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |  353         List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | 
|  359         inputs[0] = nullConstant; |  354         inputs[0] = nullConstant; | 
|  360         HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( |  355         HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( | 
|  361             user.selector, user.mask, |  356             user.selector, | 
|  362             inputs, user.instructionType, interceptedClasses); |  357             user.mask, | 
 |  358             inputs, | 
 |  359             user.instructionType, | 
 |  360             interceptedClasses); | 
|  363         oneShotInterceptor.sourceInformation = user.sourceInformation; |  361         oneShotInterceptor.sourceInformation = user.sourceInformation; | 
|  364         oneShotInterceptor.sourceElement = user.sourceElement; |  362         oneShotInterceptor.sourceElement = user.sourceElement; | 
|  365         return replaceUserWith(oneShotInterceptor); |  363         return replaceUserWith(oneShotInterceptor); | 
|  366       } |  364       } | 
|  367     } |  365     } | 
|  368  |  366  | 
|  369     return false; |  367     return false; | 
|  370   } |  368   } | 
|  371  |  369  | 
|  372   bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |  370   bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { | 
|  373     for (HInstruction user in node.usedBy.toList()) { |  371     for (HInstruction user in node.usedBy.toList()) { | 
|  374       if (user is HIs) { |  372       if (user is HIs) { | 
|  375         user.changeUse(node, receiver); |  373         user.changeUse(node, receiver); | 
|  376       } else { |  374       } else { | 
|  377         // Use the potentially self-argument as new receiver. Note that the |  375         // Use the potentially self-argument as new receiver. Note that the | 
|  378         // self-argument could potentially have a tighter type than the |  376         // self-argument could potentially have a tighter type than the | 
|  379         // receiver which was the input to the interceptor. |  377         // receiver which was the input to the interceptor. | 
|  380         assert(user.inputs[0] == node); |  378         assert(user.inputs[0] == node); | 
|  381         assert(receiver.nonCheck() == user.inputs[1].nonCheck()); |  379         assert(receiver.nonCheck() == user.inputs[1].nonCheck()); | 
|  382         user.changeUse(node, user.inputs[1]); |  380         user.changeUse(node, user.inputs[1]); | 
|  383       } |  381       } | 
|  384     } |  382     } | 
|  385     return false; |  383     return false; | 
|  386   } |  384   } | 
|  387  |  385  | 
|  388   bool visitOneShotInterceptor(HOneShotInterceptor node) { |  386   bool visitOneShotInterceptor(HOneShotInterceptor node) { | 
|  389     HInstruction constant = tryComputeConstantInterceptor( |  387     HInstruction constant = | 
|  390         node.inputs[1], node.interceptedClasses); |  388         tryComputeConstantInterceptor(node.inputs[1], node.interceptedClasses); | 
|  391  |  389  | 
|  392     if (constant == null) return false; |  390     if (constant == null) return false; | 
|  393  |  391  | 
|  394     Selector selector = node.selector; |  392     Selector selector = node.selector; | 
|  395     TypeMask mask = node.mask; |  393     TypeMask mask = node.mask; | 
|  396     HInstruction instruction; |  394     HInstruction instruction; | 
|  397     if (selector.isGetter) { |  395     if (selector.isGetter) { | 
|  398       instruction = new HInvokeDynamicGetter( |  396       instruction = new HInvokeDynamicGetter(selector, mask, node.element, | 
|  399           selector, |  397           <HInstruction>[constant, node.inputs[1]], node.instructionType); | 
|  400           mask, |  | 
|  401           node.element, |  | 
|  402           <HInstruction>[constant, node.inputs[1]], |  | 
|  403           node.instructionType); |  | 
|  404     } else if (selector.isSetter) { |  398     } else if (selector.isSetter) { | 
|  405       instruction = new HInvokeDynamicSetter( |  399       instruction = new HInvokeDynamicSetter( | 
|  406           selector, |  400           selector, | 
|  407           mask, |  401           mask, | 
|  408           node.element, |  402           node.element, | 
|  409           <HInstruction>[constant, node.inputs[1], node.inputs[2]], |  403           <HInstruction>[constant, node.inputs[1], node.inputs[2]], | 
|  410           node.instructionType); |  404           node.instructionType); | 
|  411     } else { |  405     } else { | 
|  412       List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); |  406       List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); | 
|  413       inputs[0] = constant; |  407       inputs[0] = constant; | 
|  414       instruction = new HInvokeDynamicMethod( |  408       instruction = new HInvokeDynamicMethod( | 
|  415           selector, mask, inputs, node.instructionType, true); |  409           selector, mask, inputs, node.instructionType, true); | 
|  416     } |  410     } | 
|  417  |  411  | 
|  418     HBasicBlock block = node.block; |  412     HBasicBlock block = node.block; | 
|  419     block.addAfter(node, instruction); |  413     block.addAfter(node, instruction); | 
|  420     block.rewrite(node, instruction); |  414     block.rewrite(node, instruction); | 
|  421     return true; |  415     return true; | 
|  422   } |  416   } | 
|  423 } |  417 } | 
| OLD | NEW |