Changeset 69940 in webkit


Ignore:
Timestamp:
Oct 17, 2010 9:08:37 PM (14 years ago)
Author:
oliver@apple.com
Message:

2010-10-17 Oliver Hunt <oliver@apple.com>

Reviewed by Sam Weinig.

Strict mode: Assignment that would create a global should be a late ReferenceError, not a syntax failure
https://bugs.webkit.org/show_bug.cgi?id=47788

Fixing this required a couple of changes:

  • resolve_base now has a flag to indicate whether it is being used for a put in strict mode. this allows us to throw an exception when we're doing a completely generic resolve for assignment, and that assignment would create a new global.
  • There is a new opcode 'op_ensure_property_exists' that is used to determine whether the property being assigned to already exists on the global object. This currently has no caching, but such caching could be added relatively trivially. It is only used in the case where we know that a property will be placed on the global object, and we cannot verify that the property already exists.

In the jit we plant a call to cti_op_resolve_base_strict_put in the effected case rather
than making op_resolve_base have an additional runtime branch.

There's also a new helper function to create the exception for the invalid assignment.

  • bytecode/CodeBlock.cpp: (JSC::CodeBlock::dump):
  • bytecode/Opcode.h:
  • bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitResolveBase): (JSC::BytecodeGenerator::emitResolveBaseForPut):
  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp: (JSC::AssignResolveNode::emitBytecode): (JSC::ForInNode::emitBytecode):
  • interpreter/Interpreter.cpp: (JSC::Interpreter::resolveBase): (JSC::Interpreter::privateExecute):
  • jit/JIT.cpp: (JSC::JIT::privateCompileMainPass):
  • jit/JIT.h:
  • jit/JITOpcodes.cpp: (JSC::JIT::emit_op_resolve_base): (JSC::JIT::emit_op_ensure_property_exists):
  • jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_resolve_base): (JSC::JIT::emit_op_ensure_property_exists):
  • jit/JITStubs.cpp: (JSC::DEFINE_STUB_FUNCTION):
  • jit/JITStubs.h:
  • parser/JSParser.cpp: (JSC::JSParser::parseProgram):
  • runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment):
  • runtime/ExceptionHelpers.h:
  • runtime/Operations.h: (JSC::resolveBase):

2010-10-17 Oliver Hunt <oliver@apple.com>

Reviewed by Sam Weinig.

Strict mode: Assignment that would create a global should be a late ReferenceError, not a syntax failure
https://bugs.webkit.org/show_bug.cgi?id=47788

Update test to check for the correct behaviour.

  • fast/js/basic-strict-mode-expected.txt:
  • fast/js/script-tests/basic-strict-mode.js:
Location:
trunk
Files:
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/ChangeLog

    r69929 r69940  
     12010-10-17  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Sam Weinig.
     4
     5        Strict mode: Assignment that would create a global should be a late ReferenceError, not a syntax failure
     6        https://bugs.webkit.org/show_bug.cgi?id=47788
     7
     8        Fixing this required a couple of changes:
     9         * resolve_base now has a flag to indicate whether it is being used for a put in strict mode.
     10           this allows us to throw an exception when we're doing a completely generic resolve for
     11           assignment, and that assignment would create a new global.
     12         * There is a new opcode 'op_ensure_property_exists' that is used to determine whether
     13           the property being assigned to already exists on the global object.  This currently
     14           has no caching, but such caching could be added relatively trivially.  It is only used
     15           in the case where we know that a property will be placed on the global object, and
     16           we cannot verify that the property already exists.
     17
     18        In the jit we plant a call to cti_op_resolve_base_strict_put in the effected case rather
     19        than making op_resolve_base have an additional runtime branch.
     20
     21        There's also a new helper function to create the exception for the invalid assignment.
     22
     23        * bytecode/CodeBlock.cpp:
     24        (JSC::CodeBlock::dump):
     25        * bytecode/Opcode.h:
     26        * bytecompiler/BytecodeGenerator.cpp:
     27        (JSC::BytecodeGenerator::emitResolveBase):
     28        (JSC::BytecodeGenerator::emitResolveBaseForPut):
     29        * bytecompiler/BytecodeGenerator.h:
     30        * bytecompiler/NodesCodegen.cpp:
     31        (JSC::AssignResolveNode::emitBytecode):
     32        (JSC::ForInNode::emitBytecode):
     33        * interpreter/Interpreter.cpp:
     34        (JSC::Interpreter::resolveBase):
     35        (JSC::Interpreter::privateExecute):
     36        * jit/JIT.cpp:
     37        (JSC::JIT::privateCompileMainPass):
     38        * jit/JIT.h:
     39        * jit/JITOpcodes.cpp:
     40        (JSC::JIT::emit_op_resolve_base):
     41        (JSC::JIT::emit_op_ensure_property_exists):
     42        * jit/JITOpcodes32_64.cpp:
     43        (JSC::JIT::emit_op_resolve_base):
     44        (JSC::JIT::emit_op_ensure_property_exists):
     45        * jit/JITStubs.cpp:
     46        (JSC::DEFINE_STUB_FUNCTION):
     47        * jit/JITStubs.h:
     48        * parser/JSParser.cpp:
     49        (JSC::JSParser::parseProgram):
     50        * runtime/ExceptionHelpers.cpp:
     51        (JSC::createErrorForInvalidGlobalAssignment):
     52        * runtime/ExceptionHelpers.h:
     53        * runtime/Operations.h:
     54        (JSC::resolveBase):
     55
    1562010-10-17  Simon Fraser  <simon.fraser@apple.com>
    257
  • trunk/JavaScriptCore/bytecode/CodeBlock.cpp

    r69665 r69940  
    762762            int r0 = (++it)->u.operand;
    763763            int id0 = (++it)->u.operand;
    764             printf("[%4d] resolve_base\t %s, %s\n", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data());
     764            int isStrict = (++it)->u.operand;
     765            printf("[%4d] resolve_base%s\t %s, %s\n", location, isStrict ? "_strict" : "", registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data());
     766            break;
     767        }
     768        case op_ensure_property_exists: {
     769            int r0 = (++it)->u.operand;
     770            int id0 = (++it)->u.operand;
     771            printf("[%4d] ensure_property_exists\t %s, %s\n", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data());
    765772            break;
    766773        }
  • trunk/JavaScriptCore/bytecode/Opcode.h

    r69516 r69940  
    101101        macro(op_get_global_var, 3) \
    102102        macro(op_put_global_var, 3) \
    103         macro(op_resolve_base, 3) \
     103        macro(op_resolve_base, 4) \
     104        macro(op_ensure_property_exists, 3) \
    104105        macro(op_resolve_with_base, 4) \
    105106        macro(op_get_by_id, 8) \
  • trunk/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r69516 r69940  
    12721272        instructions().append(dst->index());
    12731273        instructions().append(addConstant(property));
     1274        instructions().append(false);
    12741275        return dst;
    12751276    }
     
    12771278    // Global object is the base
    12781279    return emitLoad(dst, JSValue(globalObject));
     1280}
     1281
     1282RegisterID* BytecodeGenerator::emitResolveBaseForPut(RegisterID* dst, const Identifier& property)
     1283{
     1284    if (!m_codeBlock->isStrictMode())
     1285        return emitResolveBase(dst, property);
     1286    size_t depth = 0;
     1287    int index = 0;
     1288    JSObject* globalObject = 0;
     1289    bool requiresDynamicChecks = false;
     1290    findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject);
     1291    if (!globalObject || requiresDynamicChecks) {
     1292        // We can't optimise at all :-(
     1293        emitOpcode(op_resolve_base);
     1294        instructions().append(dst->index());
     1295        instructions().append(addConstant(property));
     1296        instructions().append(true);
     1297        return dst;
     1298    }
     1299   
     1300    // Global object is the base
     1301    RefPtr<RegisterID> result = emitLoad(dst, JSValue(globalObject));
     1302    emitOpcode(op_ensure_property_exists);
     1303    instructions().append(dst->index());
     1304    instructions().append(addConstant(property));
     1305    return result.get();
    12791306}
    12801307
  • trunk/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r69516 r69940  
    330330
    331331        RegisterID* emitResolveBase(RegisterID* dst, const Identifier& property);
     332        RegisterID* emitResolveBaseForPut(RegisterID* dst, const Identifier& property);
    332333        RegisterID* emitResolveWithBase(RegisterID* baseDst, RegisterID* propDst, const Identifier& property);
    333334
  • trunk/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r69663 r69940  
    12321232    }
    12331233
    1234     RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident);
     1234    RefPtr<RegisterID> base = generator.emitResolveBaseForPut(generator.newTemporary(), m_ident);
    12351235    if (dst == generator.ignoredResult())
    12361236        dst = 0;
     
    16061606            propertyName = generator.newTemporary();
    16071607            RefPtr<RegisterID> protect = propertyName;
    1608             RegisterID* base = generator.emitResolveBase(generator.newTemporary(), ident);
     1608            RegisterID* base = generator.emitResolveBaseForPut(generator.newTemporary(), ident);
    16091609
    16101610            generator.emitExpressionInfo(divot(), startOffset(), endOffset());
  • trunk/JavaScriptCore/interpreter/Interpreter.cpp

    r69522 r69940  
    281281    int dst = vPC[1].u.operand;
    282282    int property = vPC[2].u.operand;
    283     callFrame->r(dst) = JSValue(JSC::resolveBase(callFrame, callFrame->codeBlock()->identifier(property), callFrame->scopeChain()));
    284     ASSERT(callFrame->r(dst).jsValue());
     283    bool isStrictPut = vPC[3].u.operand;
     284    const Identifier ident = callFrame->codeBlock()->identifier(property);
     285    JSValue result = JSC::resolveBase(callFrame, ident, callFrame->scopeChain(), isStrictPut);
     286    if (!result) {
     287        callFrame->r(dst) = result;
     288        ASSERT(callFrame->r(dst).jsValue());
     289    } else
     290        callFrame->globalData().exception = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring());
    285291}
    286292
     
    24312437
    24322438        vPC += OPCODE_LENGTH(op_resolve_base);
     2439        NEXT_INSTRUCTION();
     2440    }
     2441    DEFINE_OPCODE(op_ensure_property_exists) {
     2442        /* ensure_property_exists base(r) property(id)
     2443
     2444           Throws an exception if property does not exist on base
     2445         */
     2446        int base = vPC[1].u.operand;
     2447        int property = vPC[2].u.operand;
     2448        Identifier& ident = codeBlock->identifier(property);
     2449       
     2450        JSValue baseVal = callFrame->r(base).jsValue();
     2451        JSObject* baseObject = asObject(baseVal);
     2452        PropertySlot slot(baseVal);
     2453        if (!baseObject->getPropertySlot(callFrame, ident, slot)) {
     2454            exceptionValue = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring());
     2455            goto vm_throw;
     2456        }
    24332457        NEXT_INSTRUCTION();
    24342458    }
  • trunk/JavaScriptCore/jit/JIT.cpp

    r69516 r69940  
    303303        DEFINE_OP(op_resolve)
    304304        DEFINE_OP(op_resolve_base)
     305        DEFINE_OP(op_ensure_property_exists)
    305306        DEFINE_OP(op_resolve_global)
    306307        DEFINE_OP(op_resolve_global_dynamic)
  • trunk/JavaScriptCore/jit/JIT.h

    r69516 r69940  
    812812        void emit_op_resolve(Instruction*);
    813813        void emit_op_resolve_base(Instruction*);
     814        void emit_op_ensure_property_exists(Instruction*);
    814815        void emit_op_resolve_global(Instruction*, bool dynamic = false);
    815816        void emit_op_resolve_global_dynamic(Instruction*);
  • trunk/JavaScriptCore/jit/JITOpcodes.cpp

    r69516 r69940  
    645645void JIT::emit_op_resolve_base(Instruction* currentInstruction)
    646646{
    647     JITStubCall stubCall(this, cti_op_resolve_base);
     647    JITStubCall stubCall(this, currentInstruction[3].u.operand ? cti_op_resolve_base_strict_put : cti_op_resolve_base);
     648    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
     649    stubCall.call(currentInstruction[1].u.operand);
     650}
     651
     652void JIT::emit_op_ensure_property_exists(Instruction* currentInstruction)
     653{
     654    JITStubCall stubCall(this, cti_op_ensure_property_exists);
     655    stubCall.addArgument(Imm32(currentInstruction[1].u.operand));
    648656    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
    649657    stubCall.call(currentInstruction[1].u.operand);
  • trunk/JavaScriptCore/jit/JITOpcodes32_64.cpp

    r69533 r69940  
    738738void JIT::emit_op_resolve_base(Instruction* currentInstruction)
    739739{
    740     JITStubCall stubCall(this, cti_op_resolve_base);
     740    JITStubCall stubCall(this, currentInstruction[3].u.operand ? cti_op_resolve_base_strict_put : cti_op_resolve_base);
     741    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
     742    stubCall.call(currentInstruction[1].u.operand);
     743}
     744
     745void JIT::emit_op_ensure_property_exists(Instruction* currentInstruction)
     746{
     747    JITStubCall stubCall(this, cti_op_ensure_property_exists);
     748    stubCall.addArgument(Imm32(currentInstruction[1].u.operand));
    741749    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
    742750    stubCall.call(currentInstruction[1].u.operand);
  • trunk/JavaScriptCore/jit/JITStubs.cpp

    r69516 r69940  
    26982698    STUB_INIT_STACK_FRAME(stackFrame);
    26992699
    2700     return JSValue::encode(JSC::resolveBase(stackFrame.callFrame, stackFrame.args[0].identifier(), stackFrame.callFrame->scopeChain()));
    2701 }
    2702 
     2700    return JSValue::encode(JSC::resolveBase(stackFrame.callFrame, stackFrame.args[0].identifier(), stackFrame.callFrame->scopeChain(), false));
     2701}
     2702
     2703DEFINE_STUB_FUNCTION(EncodedJSValue, op_resolve_base_strict_put)
     2704{
     2705    STUB_INIT_STACK_FRAME(stackFrame);
     2706    JSValue base = JSC::resolveBase(stackFrame.callFrame, stackFrame.args[0].identifier(), stackFrame.callFrame->scopeChain(), true);
     2707    if (!base) {
     2708        stackFrame.globalData->exception = createErrorForInvalidGlobalAssignment(stackFrame.callFrame, stackFrame.args[0].identifier().ustring());
     2709        VM_THROW_EXCEPTION();
     2710    }
     2711    return JSValue::encode(base);
     2712}
     2713
     2714DEFINE_STUB_FUNCTION(EncodedJSValue, op_ensure_property_exists)
     2715{
     2716    STUB_INIT_STACK_FRAME(stackFrame);
     2717    JSValue base = stackFrame.callFrame->r(stackFrame.args[0].int32()).jsValue();
     2718    JSObject* object = asObject(base);
     2719    PropertySlot slot(object);
     2720    ASSERT(stackFrame.callFrame->codeBlock()->isStrictMode());
     2721    if (!object->getPropertySlot(stackFrame.callFrame, stackFrame.args[1].identifier(), slot)) {
     2722        stackFrame.globalData->exception = createErrorForInvalidGlobalAssignment(stackFrame.callFrame, stackFrame.args[1].identifier().ustring());
     2723        VM_THROW_EXCEPTION();
     2724    }
     2725
     2726    return JSValue::encode(base);
     2727}
     2728   
    27032729DEFINE_STUB_FUNCTION(EncodedJSValue, op_resolve_skip)
    27042730{
  • trunk/JavaScriptCore/jit/JITStubs.h

    r69516 r69940  
    339339    EncodedJSValue JIT_STUB cti_op_resolve(STUB_ARGS_DECLARATION);
    340340    EncodedJSValue JIT_STUB cti_op_resolve_base(STUB_ARGS_DECLARATION);
     341    EncodedJSValue JIT_STUB cti_op_resolve_base_strict_put(STUB_ARGS_DECLARATION);
     342    EncodedJSValue JIT_STUB cti_op_ensure_property_exists(STUB_ARGS_DECLARATION);
    341343    EncodedJSValue JIT_STUB cti_op_resolve_global(STUB_ARGS_DECLARATION);
    342344    EncodedJSValue JIT_STUB cti_op_resolve_global_dynamic(STUB_ARGS_DECLARATION);
  • trunk/JavaScriptCore/parser/JSParser.cpp

    r69906 r69940  
    530530        return true;
    531531    if (!m_syntaxAlreadyValidated) {
    532         IdentifierSet writtenVariables;
    533         scope->getUncapturedWrittenVariables(writtenVariables);
    534         IdentifierSet::const_iterator end = writtenVariables.end();
    535         for (IdentifierSet::const_iterator ptr = writtenVariables.begin(); ptr != end; ++ptr) {
    536             PropertySlot slot(lexicalGlobalObject);
    537             if (!lexicalGlobalObject->getPropertySlot(lexicalGlobalObject->globalExec(), Identifier(m_globalData, *ptr), slot))
    538                 return true;
    539         }
    540532        IdentifierSet deletedVariables;
    541533        if (!scope->getDeletedVariables(deletedVariables))
    542534            return true;
    543         end = deletedVariables.end();
     535        IdentifierSet::const_iterator end = deletedVariables.end();
    544536        SymbolTable& globalEnvRecord = lexicalGlobalObject->symbolTable();
    545537        for (IdentifierSet::const_iterator ptr = deletedVariables.begin(); ptr != end; ++ptr) {
  • trunk/JavaScriptCore/runtime/ExceptionHelpers.cpp

    r69663 r69940  
    193193}
    194194
     195JSObject* createErrorForInvalidGlobalAssignment(ExecState* exec, const UString& propertyName)
     196{
     197    return createReferenceError(exec, makeUString("Strict mode forbids implicit creation of global property '", propertyName, "'"));
     198}   
     199
    195200} // namespace JSC
  • trunk/JavaScriptCore/runtime/ExceptionHelpers.h

    r66150 r69940  
    5555    JSObject* createNotAnObjectError(ExecState*, JSNotAnObjectErrorStub*, unsigned bytecodeOffset, CodeBlock*);
    5656    JSValue throwOutOfMemoryError(ExecState*);
     57    JSObject* createErrorForInvalidGlobalAssignment(ExecState*, const UString&);
    5758
    5859} // namespace JSC
  • trunk/JavaScriptCore/runtime/Operations.h

    r65177 r69940  
    461461    }
    462462
    463     ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
     463    ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut)
    464464    {
    465465        ScopeChainIterator iter = scopeChain->begin();
     
    473473        while (true) {
    474474            base = *iter;
    475             if (next == end || base->getPropertySlot(callFrame, property, slot))
     475            if (next == end)
     476                return isStrictPut ? JSValue() : base;
     477            if (base->getPropertySlot(callFrame, property, slot))
    476478                return base;
    477479
  • trunk/LayoutTests/ChangeLog

    r69939 r69940  
     12010-10-17  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Sam Weinig.
     4
     5        Strict mode: Assignment that would create a global should be a late ReferenceError, not a syntax failure
     6        https://bugs.webkit.org/show_bug.cgi?id=47788
     7
     8        Update test to check for the correct behaviour.
     9
     10        * fast/js/basic-strict-mode-expected.txt:
     11        * fast/js/script-tests/basic-strict-mode.js:
     12
    1132010-10-17  Antonio Gomes  <agomes@rim.com>
    214
  • trunk/LayoutTests/fast/js/basic-strict-mode-expected.txt

    r69516 r69940  
    5252PASS 'use strict'; (function (){ delete someDeclaredGlobal;}) threw exception SyntaxError: Parse error.
    5353PASS (function (){ 'use strict'; delete someDeclaredGlobal;}) threw exception SyntaxError: Parse error.
    54 PASS 'use strict'; someGlobal = 'Shouldn\'t be able to assign this.'; someGlobal; threw exception SyntaxError: Parse error.
     54PASS 'use strict'; if (0) { someGlobal = 'Shouldn\'t be able to assign this.'; }; true; is true
     55PASS 'use strict'; someGlobal = 'Shouldn\'t be able to assign this.';  threw exception ReferenceError: Strict mode forbids implicit creation of global property 'someGlobal'.
    5556PASS 'use strict'; eval('var introducedVariable = "FAIL: variable introduced into containing scope";'); introducedVariable threw exception ReferenceError: Can't find variable: introducedVariable.
    5657PASS 'use strict'; objectWithReadonlyProperty.prop = 'fail' threw exception TypeError: Attempted to assign to readonly property..
  • trunk/LayoutTests/fast/js/script-tests/basic-strict-mode.js

    r69516 r69940  
    6161shouldThrow("'use strict'; (function (){ delete someDeclaredGlobal;})");
    6262shouldThrow("(function (){ 'use strict'; delete someDeclaredGlobal;})");
    63 shouldThrow("'use strict'; someGlobal = 'Shouldn\\'t be able to assign this.'; someGlobal;");
     63shouldBeTrue("'use strict'; if (0) { someGlobal = 'Shouldn\\'t be able to assign this.'; }; true;");
     64shouldThrow("'use strict'; someGlobal = 'Shouldn\\'t be able to assign this.'; ");
    6465shouldThrow("'use strict'; eval('var introducedVariable = \"FAIL: variable introduced into containing scope\";'); introducedVariable");
    6566var objectWithReadonlyProperty = {};
Note: See TracChangeset for help on using the changeset viewer.