| Index: sdk/lib/_internal/compiler/implementation/js_backend/backend.dart | 
| =================================================================== | 
| --- sdk/lib/_internal/compiler/implementation/js_backend/backend.dart	(revision 14840) | 
| +++ sdk/lib/_internal/compiler/implementation/js_backend/backend.dart	(working copy) | 
| @@ -646,6 +646,10 @@ | 
| SsaCodeGeneratorTask generator; | 
| CodeEmitterTask emitter; | 
|  | 
| +  ClassElement jsStringClass; | 
| +  ClassElement objectInterceptorClass; | 
| +  Element getInterceptorMethod; | 
| + | 
| final Namer namer; | 
|  | 
| /** | 
| @@ -668,6 +672,20 @@ | 
|  | 
| final Interceptors interceptors; | 
|  | 
| +  /** | 
| +   * A collection of selectors of intercepted method calls. The | 
| +   * emitter uses this set to generate the [:ObjectInterceptor:] class | 
| +   * whose members just forward the call to the intercepted receiver. | 
| +   */ | 
| +  final Set<Selector> usedInterceptors; | 
| + | 
| +  /** | 
| +   * The members of instantiated interceptor classes: maps a member | 
| +   * name to the list of members that have that name. This map is used | 
| +   * by the codegen to know whether a send must be intercepted or not. | 
| +   */ | 
| +  final Map<SourceString, List<Element>> interceptedElements; | 
| + | 
| List<CompilerTask> get tasks { | 
| return <CompilerTask>[builder, optimizer, generator, emitter]; | 
| } | 
| @@ -679,6 +697,8 @@ | 
| returnInfo = new Map<Element, ReturnInfo>(), | 
| invalidateAfterCodegen = new List<Element>(), | 
| interceptors = new Interceptors(compiler), | 
| +        usedInterceptors = new Set<Selector>(), | 
| +        interceptedElements = new Map<SourceString, List<Element>>(), | 
| super(compiler, JAVA_SCRIPT_CONSTANT_SYSTEM) { | 
| emitter = disableEval | 
| ? new CodeEmitterNoEvalTask(compiler, namer, generateSourceMap) | 
| @@ -690,6 +710,49 @@ | 
| fieldTypes = new FieldTypesRegistry(this); | 
| } | 
|  | 
| +  bool isInterceptorClass(Element element) { | 
| +    return element == jsStringClass; | 
| +  } | 
| + | 
| +  void addInterceptedSelector(Selector selector) { | 
| +    usedInterceptors.add(selector); | 
| +  } | 
| + | 
| +  bool shouldInterceptSelector(Selector selector) { | 
| +    List<Element> intercepted = interceptedElements[selector.name]; | 
| +    if (intercepted == null) return false; | 
| +    for (Element element in intercepted) { | 
| +      if (selector.applies(element, compiler)) return true; | 
| +    } | 
| +    return false; | 
| +  } | 
| + | 
| + | 
| +  void registerInstantiatedClass(ClassElement cls, Enqueuer enqueuer) { | 
| +    ClassElement result = null; | 
| +    if (cls == compiler.stringClass) { | 
| +      if (jsStringClass == null) { | 
| +        jsStringClass = | 
| +            compiler.findInterceptor(const SourceString('JSString')); | 
| +        objectInterceptorClass = | 
| +            compiler.findInterceptor(const SourceString('ObjectInterceptor')); | 
| +        getInterceptorMethod = | 
| +            compiler.findInterceptor(const SourceString('getInterceptor')); | 
| +      } | 
| +      result = jsStringClass; | 
| +    } | 
| + | 
| +    if (result == null) return; | 
| + | 
| +    result.forEachMember((_, Element member) { | 
| +      List<Element> list = interceptedElements.putIfAbsent( | 
| +          member.name, () => new List<Element>()); | 
| +      list.add(member); | 
| +    }); | 
| + | 
| +    enqueuer.registerInstantiatedClass(result); | 
| +  } | 
| + | 
| Element get cyclicThrowHelper { | 
| return compiler.findHelper(const SourceString("throwCyclicInit")); | 
| } | 
|  |