Changeset 198332 in webkit


Ignore:
Timestamp:
Mar 17, 2016, 7:58:57 AM (9 years ago)
Author:
mark.lam@apple.com
Message:

Method names should not appear in the lexical scope of the method's body.
https://bugs.webkit.org/show_bug.cgi?id=155568

Reviewed by Saam Barati.

Source/JavaScriptCore:

Consider this scenario:

var f = "foo";
var result = ({

f() {

return f; f should be the string "foo", not this method f.

}

}).f();
result === "foo"; Should be true.

The reason this is not current working is because the parser does not yet
distinguish between FunctionExpressions and MethodDefinitions. The ES6 spec
explicitly distinguishes between the 2, and we should do the same.

This patch changes all methods (and getters and setters which are also methods)
to have a FunctionMode of MethodDefinition (instead of FunctionExpression).
functionNameIsInScope() is responsible for determining whether a function's name
should be in its scope or not. It already returns false for any function
whose FunctionMode is not FunctionExpression. Giving methods the MethodDefinition
FunctionMode gets us the correct behavior ES6 expects.

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):

  • bytecode/UnlinkedFunctionExecutable.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
(JSC::BytecodeGenerator::emitNewMethodDefinition):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ArrowFuncExprNode::emitBytecode):
(JSC::MethodDefinitionNode::emitBytecode):
(JSC::YieldExprNode::emitBytecode):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createFunctionExpr):
(JSC::ASTBuilder::createMethodDefinition):
(JSC::ASTBuilder::createFunctionMetadata):
(JSC::ASTBuilder::createGetterOrSetterProperty):
(JSC::ASTBuilder::createArguments):

  • parser/NodeConstructors.h:

(JSC::FunctionParameters::FunctionParameters):
(JSC::BaseFuncExprNode::BaseFuncExprNode):
(JSC::FuncExprNode::FuncExprNode):
(JSC::FuncDeclNode::FuncDeclNode):
(JSC::ArrowFuncExprNode::ArrowFuncExprNode):
(JSC::MethodDefinitionNode::MethodDefinitionNode):
(JSC::YieldExprNode::YieldExprNode):

  • parser/Nodes.h:

(JSC::BaseFuncExprNode::metadata):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parsePropertyMethod):

  • parser/ParserModes.h:
  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createFunctionExpr):
(JSC::SyntaxChecker::createFunctionMetadata):
(JSC::SyntaxChecker::createArrowFunctionExpr):
(JSC::SyntaxChecker::createMethodDefinition):
(JSC::SyntaxChecker::setFunctionNameStart):
(JSC::SyntaxChecker::createArguments):

  • tests/es6.yaml:

LayoutTests:

  • inspector/model/scope-chain-node-expected.txt:
  • rebased expected result.
  • js/script-tests/function-toString-vs-name.js:
  • fixed a bug in the shouldBe() function.
  • js/methods-names-should-not-be-in-lexical-scope-expected.txt: Added.
  • js/methods-names-should-not-be-in-lexical-scope.html: Added.
  • js/script-tests/methods-names-should-not-be-in-lexical-scope.js: Added.
  • test all variations of methods.
Location:
trunk
Files:
3 added
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r198327 r198332  
     12016-03-17  Mark Lam  <mark.lam@apple.com>
     2
     3        Method names should not appear in the lexical scope of the method's body.
     4        https://bugs.webkit.org/show_bug.cgi?id=155568
     5
     6        Reviewed by Saam Barati.
     7
     8        * inspector/model/scope-chain-node-expected.txt:
     9        - rebased expected result.
     10
     11        * js/script-tests/function-toString-vs-name.js:
     12        - fixed a bug in the shouldBe() function.
     13
     14        * js/methods-names-should-not-be-in-lexical-scope-expected.txt: Added.
     15        * js/methods-names-should-not-be-in-lexical-scope.html: Added.
     16        * js/script-tests/methods-names-should-not-be-in-lexical-scope.js: Added.
     17        - test all variations of methods.
     18
    1192016-03-17  Csaba Osztrogonác  <ossy@webkit.org>
    220
  • trunk/LayoutTests/inspector/model/scope-chain-node-expected.txt

    r194251 r198332  
    6666SCOPE CHAIN:
    6767    Closure
    68     FunctionName
    69       - staticMethod: function staticMethod() {
    7068    Block
    7169      - MyClass: class MyClass
     
    110108SCOPE CHAIN:
    111109    Closure
    112     FunctionName
    113       - method: function method() {
    114110    Block
    115111      - MyClass: class MyClass
     
    123119SCOPE CHAIN:
    124120    Closure
    125     FunctionName
    126       - staticMethod: function staticMethod() {
    127121    Block
    128122      - MyClassWithStaticMethod: class MyClassWithStaticMethod
  • trunk/LayoutTests/js/script-tests/function-toString-vs-name.js

    r198288 r198332  
    5151
    5252function shouldBe(desc, funcName, actual, expected) {
    53     if (typeof(actual) !== typeof(expected)) {
     53    if (typeof(actual) !== typeof(expected) || actual !== expected) {
    5454        failures += ("   " + section + ": " + desc + "'" + funcName + "': typeof expected: " + typeof(expected) + ", typeof actual: " + typeof(actual) + "\n");
    5555        failures += ("       expected: '" + expected + "', actual: '" + actual + "'\n");
    56         failureCount++;
    57     } else if (typeof(actual) !== typeof(expected) || actual !== expected) {
    58         failures += ("   " + section + ": " + desc + "'" + funcName + "': expected: '" + expected + "', actual: '" + actual + "'\n");
    5956        failureCount++;
    6057    }
  • trunk/Source/JavaScriptCore/ChangeLog

    r198331 r198332  
     12016-03-16  Mark Lam  <mark.lam@apple.com>
     2
     3        Method names should not appear in the lexical scope of the method's body.
     4        https://bugs.webkit.org/show_bug.cgi?id=155568
     5
     6        Reviewed by Saam Barati.
     7
     8        Consider this scenario:
     9
     10            var f = "foo";
     11            var result = ({
     12                f() {
     13                    return f; // f should be the string "foo", not this method f.
     14                }
     15            }).f();
     16            result === "foo"; // Should be true.
     17
     18        The reason this is not current working is because the parser does not yet
     19        distinguish between FunctionExpressions and MethodDefinitions.  The ES6 spec
     20        explicitly distinguishes between the 2, and we should do the same.
     21       
     22        This patch changes all methods (and getters and setters which are also methods)
     23        to have a FunctionMode of MethodDefinition (instead of FunctionExpression).
     24        functionNameIsInScope() is responsible for determining whether a function's name
     25        should be in its scope or not.  It already returns false for any function
     26        whose FunctionMode is not FunctionExpression.  Giving methods the MethodDefinition
     27        FunctionMode gets us the correct behavior ES6 expects.
     28
     29        * bytecode/UnlinkedFunctionExecutable.cpp:
     30        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
     31        * bytecode/UnlinkedFunctionExecutable.h:
     32        * bytecompiler/BytecodeGenerator.cpp:
     33        (JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
     34        (JSC::BytecodeGenerator::emitNewMethodDefinition):
     35        * bytecompiler/BytecodeGenerator.h:
     36        * bytecompiler/NodesCodegen.cpp:
     37        (JSC::ArrowFuncExprNode::emitBytecode):
     38        (JSC::MethodDefinitionNode::emitBytecode):
     39        (JSC::YieldExprNode::emitBytecode):
     40        * parser/ASTBuilder.h:
     41        (JSC::ASTBuilder::createFunctionExpr):
     42        (JSC::ASTBuilder::createMethodDefinition):
     43        (JSC::ASTBuilder::createFunctionMetadata):
     44        (JSC::ASTBuilder::createGetterOrSetterProperty):
     45        (JSC::ASTBuilder::createArguments):
     46        * parser/NodeConstructors.h:
     47        (JSC::FunctionParameters::FunctionParameters):
     48        (JSC::BaseFuncExprNode::BaseFuncExprNode):
     49        (JSC::FuncExprNode::FuncExprNode):
     50        (JSC::FuncDeclNode::FuncDeclNode):
     51        (JSC::ArrowFuncExprNode::ArrowFuncExprNode):
     52        (JSC::MethodDefinitionNode::MethodDefinitionNode):
     53        (JSC::YieldExprNode::YieldExprNode):
     54        * parser/Nodes.h:
     55        (JSC::BaseFuncExprNode::metadata):
     56        * parser/Parser.cpp:
     57        (JSC::Parser<LexerType>::parseClass):
     58        (JSC::Parser<LexerType>::parsePropertyMethod):
     59        * parser/ParserModes.h:
     60        * parser/SyntaxChecker.h:
     61        (JSC::SyntaxChecker::createFunctionExpr):
     62        (JSC::SyntaxChecker::createFunctionMetadata):
     63        (JSC::SyntaxChecker::createArrowFunctionExpr):
     64        (JSC::SyntaxChecker::createMethodDefinition):
     65        (JSC::SyntaxChecker::setFunctionNameStart):
     66        (JSC::SyntaxChecker::createArguments):
     67        * tests/es6.yaml:
     68
    1692016-03-17  Yusuke Suzuki  <utatane.tea@gmail.com>
    270
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp

    r198042 r198332  
    108108    , m_classSource(node->classSource())
    109109{
     110    // Make sure these bitfields are adequately wide.
     111    ASSERT(m_constructAbility == static_cast<unsigned>(constructAbility));
    110112    ASSERT(m_constructorKind == static_cast<unsigned>(node->constructorKind()));
     113    ASSERT(m_functionMode == node->functionMode());
     114    ASSERT(m_superBinding == static_cast<unsigned>(node->superBinding()));
     115    ASSERT(m_derivedContextType == static_cast<unsigned>(derivedContextType));
     116    ASSERT(m_sourceParseMode == static_cast<unsigned>(node->parseMode()));
     117
    111118    m_parentScopeTDZVariables.swap(parentScopeTDZVariables);
    112119}
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h

    r198042 r198332  
    157157    unsigned m_constructAbility: 1;
    158158    unsigned m_constructorKind : 2;
    159     unsigned m_functionMode : 1; // FunctionMode
     159    unsigned m_functionMode : 2; // FunctionMode
    160160    unsigned m_superBinding : 1;
    161161    unsigned m_derivedContextType: 2;
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r198324 r198332  
    27922792RegisterID* BytecodeGenerator::emitNewArrowFunctionExpression(RegisterID* dst, ArrowFuncExprNode* func)
    27932793{
    2794     ASSERT(func->metadata()->parseMode() == SourceParseMode::ArrowFunctionMode);   
     2794    ASSERT(func->metadata()->parseMode() == SourceParseMode::ArrowFunctionMode);
     2795    emitNewFunctionExpressionCommon(dst, func->metadata());
     2796    return dst;
     2797}
     2798
     2799RegisterID* BytecodeGenerator::emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode* func)
     2800{
     2801    ASSERT(func->metadata()->parseMode() == SourceParseMode::GeneratorWrapperFunctionMode
     2802        || func->metadata()->parseMode() == SourceParseMode::GetterMode
     2803        || func->metadata()->parseMode() == SourceParseMode::SetterMode
     2804        || func->metadata()->parseMode() == SourceParseMode::MethodMode);
    27952805    emitNewFunctionExpressionCommon(dst, func->metadata());
    27962806    return dst;
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r198288 r198332  
    518518
    519519        RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*);
    520         RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func);
     520        RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*);
    521521        RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource);
    522522        RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*);
     523        RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*);
    523524        RegisterID* emitNewRegExp(RegisterID* dst, RegExp*);
    524525
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r198288 r198332  
    31763176}
    31773177
     3178// ------------------------------ MethodDefinitionNode ---------------------------------
     3179
     3180RegisterID* MethodDefinitionNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     3181{
     3182    return generator.emitNewMethodDefinition(generator.finalDestination(dst), this);
     3183}
     3184
    31783185// ------------------------------ YieldExprNode --------------------------------
    31793186
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r198144 r198332  
    381381    }
    382382
     383    ExpressionNode* createMethodDefinition(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo)
     384    {
     385        MethodDefinitionNode* result = new (m_parserArena) MethodDefinitionNode(location, *functionInfo.name, functionInfo.body,
     386            m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn));
     387        functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset);
     388        return result;
     389    }
     390   
    383391    FunctionMetadataNode* createFunctionMetadata(
    384392        const JSTokenLocation& startLocation, const JSTokenLocation& endLocation,
     
    410418        functionInfo.body->setInferredName(*name);
    411419        SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn);
    412         FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
    413         return new (m_parserArena) PropertyNode(*name, funcExpr, type, PropertyNode::Unknown, superBinding);
     420        MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
     421        return new (m_parserArena) PropertyNode(*name, methodDef, type, PropertyNode::Unknown, superBinding);
    414422    }
    415423
     
    420428        functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset);
    421429        SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn);
    422         FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
    423         return new (m_parserArena) PropertyNode(name, funcExpr, type, PropertyNode::Unknown, superBinding);
     430        MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
     431        return new (m_parserArena) PropertyNode(name, methodDef, type, PropertyNode::Unknown, superBinding);
    424432    }
    425433
     
    430438        const Identifier& ident = parserArena.identifierArena().makeNumericIdentifier(vm, name);
    431439        SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn);
    432         FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, vm->propertyNames->nullIdentifier, functionInfo.body, source);
    433         return new (m_parserArena) PropertyNode(ident, funcExpr, type, PropertyNode::Unknown, superBinding);
     440        MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, vm->propertyNames->nullIdentifier, functionInfo.body, source);
     441        return new (m_parserArena) PropertyNode(ident, methodDef, type, PropertyNode::Unknown, superBinding);
    434442    }
    435443
  • trunk/Source/JavaScriptCore/parser/NodeConstructors.h

    r198042 r198332  
    11/*
    2  *  Copyright (C) 2009, 2013, 2015 Apple Inc. All rights reserved.
     2 *  Copyright (C) 2009, 2013, 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 *  This library is free software; you can redistribute it and/or
     
    891891
    892892   
    893     inline BaseFuncExprNode::BaseFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     893    inline BaseFuncExprNode::BaseFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source, FunctionMode functionMode)
    894894        : ExpressionNode(location)
    895895        , m_metadata(metadata)
    896896    {
    897         m_metadata->finishParsing(source, ident, FunctionExpression);
     897        m_metadata->finishParsing(source, ident, functionMode);
    898898    }
    899899
    900900    inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
    901         : BaseFuncExprNode(location, ident, metadata, source)
    902     {
    903     }
    904 
     901        : BaseFuncExprNode(location, ident, metadata, source, FunctionExpression)
     902    {
     903    }
     904
     905    inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source, FunctionMode functionMode)
     906        : BaseFuncExprNode(location, ident, metadata, source, functionMode)
     907    {
     908    }
     909   
    905910    inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
    906911        : StatementNode(location)
     
    911916
    912917    inline ArrowFuncExprNode::ArrowFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
    913         : BaseFuncExprNode(location, ident, metadata, source)
    914     {
    915     }
    916 
     918        : BaseFuncExprNode(location, ident, metadata, source, FunctionExpression)
     919    {
     920    }
     921
     922    inline MethodDefinitionNode::MethodDefinitionNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source)
     923        : FuncExprNode(location, ident, metadata, source, MethodDefinition)
     924    {
     925    }
     926   
    917927    inline YieldExprNode::YieldExprNode(const JSTokenLocation& location, ExpressionNode* argument, bool delegate)
    918928        : ExpressionNode(location)
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r198288 r198332  
    19151915        const Identifier& ident() { return m_ident; }
    19161916
    1917         FunctionMode functionMode() { return m_functionMode; }
     1917        FunctionMode functionMode() const { return m_functionMode; }
    19181918
    19191919        unsigned startColumn() const { return m_startColumn; }
     
    19321932    class BaseFuncExprNode : public ExpressionNode {
    19331933    public:
    1934         BaseFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&);
    1935 
    19361934        FunctionMetadataNode* metadata() { return m_metadata; }
    19371935
    19381936    protected:
     1937        BaseFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&, FunctionMode);
     1938
    19391939        FunctionMetadataNode* m_metadata;
    19401940    };
     
    19451945        FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&);
    19461946
     1947    protected:
     1948        FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&, FunctionMode);
     1949
    19471950    private:
    19481951        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     
    19591962
    19601963        bool isArrowFuncExprNode() const override { return true; }
     1964    };
     1965
     1966    class MethodDefinitionNode : public FuncExprNode {
     1967    public:
     1968        MethodDefinitionNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&);
     1969       
     1970    private:
     1971        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
    19611972    };
    19621973
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r198324 r198332  
    23282328            methodInfo.name = isConstructor ? className : ident;
    23292329
    2330             TreeExpression method = context.createFunctionExpr(methodLocation, methodInfo);
     2330            TreeExpression method = context.createMethodDefinition(methodLocation, methodInfo);
    23312331            if (isConstructor) {
    23322332                semanticFailIfTrue(constructor, "Cannot declare multiple constructors in a single class");
     
    33153315    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method");
    33163316    methodInfo.name = methodName;
    3317     return context.createFunctionExpr(methodLocation, methodInfo);
     3317    return context.createMethodDefinition(methodLocation, methodInfo);
    33183318}
    33193319
  • trunk/Source/JavaScriptCore/parser/ParserModes.h

    r197928 r198332  
    11/*
    2  * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    4343enum DebuggerMode { DebuggerOff, DebuggerOn };
    4444
    45 enum FunctionMode { FunctionExpression, FunctionDeclaration };
     45enum FunctionMode { FunctionExpression, FunctionDeclaration, MethodDefinition };
    4646
    4747enum class SourceParseMode : uint8_t {
  • trunk/Source/JavaScriptCore/parser/SyntaxChecker.h

    r198042 r198332  
    187187    int createFunctionMetadata(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, int, int, int, ConstructorKind, SuperBinding, unsigned, SourceParseMode, bool, InnerArrowFunctionCodeFeatures = NoInnerArrowFunctionFeatures) { return FunctionBodyResult; }
    188188    ExpressionType createArrowFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; }
     189    ExpressionType createMethodDefinition(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; }
    189190    void setFunctionNameStart(int, int) { }
    190191    int createArguments() { return ArgumentsResult; }
  • trunk/Source/JavaScriptCore/tests/es6.yaml

    r198294 r198332  
    806806  cmd: runES6 :normal
    807807- path: es6/function_name_property_shorthand_methods_no_lexical_binding.js
    808   cmd: runES6 :fail
     808  cmd: runES6 :normal
    809809- path: es6/function_name_property_symbol-keyed_methods.js
    810810  cmd: runES6 :normal
Note: See TracChangeset for help on using the changeset viewer.