Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2002 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2001 Peter Kelly (pmk (at) post.com)
      4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      5  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich (at) uwaterloo.ca)
      6  *  Copyright (C) 2007 Maks Orlovich
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Library General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Library General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Library General Public License
     19  *  along with this library; see the file COPYING.LIB.  If not, write to
     20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  *  Boston, MA 02110-1301, USA.
     22  *
     23  */
     24 
     25 #include "config.h"
     26 #include "JSFunction.h"
     27 
     28 #include "CodeBlock.h"
     29 #include "CommonIdentifiers.h"
     30 #include "CallFrame.h"
     31 #include "ExceptionHelpers.h"
     32 #include "FunctionPrototype.h"
     33 #include "JSGlobalObject.h"
     34 #include "JSNotAnObject.h"
     35 #include "Interpreter.h"
     36 #include "ObjectPrototype.h"
     37 #include "Parser.h"
     38 #include "PropertyNameArray.h"
     39 #include "ScopeChainMark.h"
     40 
     41 using namespace WTF;
     42 using namespace Unicode;
     43 
     44 namespace JSC {
     45 EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec)
     46 {
     47     return throwVMError(exec, createNotAConstructorError(exec, exec->callee()));
     48 }
     49 
     50 ASSERT_CLASS_FITS_IN_CELL(JSFunction);
     51 
     52 const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0 };
     53 
     54 bool JSFunction::isHostFunctionNonInline() const
     55 {
     56     return isHostFunction();
     57 }
     58 
     59 JSFunction::JSFunction(VPtrStealingHackType)
     60     : Base(VPtrStealingHack)
     61 {
     62 }
     63 
     64 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeExecutable* thunk)
     65     : Base(globalObject, structure)
     66     , m_executable(exec->globalData(), this, thunk)
     67     , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
     68 {
     69     ASSERT(inherits(&s_info));
     70     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
     71     putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
     72 }
     73 
     74 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeFunction func)
     75     : Base(globalObject, structure)
     76     , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
     77 {
     78     ASSERT(inherits(&s_info));
     79     // We separate out intialisation from setting the executable
     80     // as getHostFunction may perform a GC allocation, so we have to be able to
     81     // mark ourselves safely
     82     m_executable.set(exec->globalData(), this, exec->globalData().getHostFunction(func));
     83     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
     84     putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
     85 }
     86 
     87 JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
     88     : Base(scopeChainNode->globalObject.get(), scopeChainNode->globalObject->functionStructure())
     89     , m_executable(exec->globalData(), this, executable)
     90     , m_scopeChain(exec->globalData(), this, scopeChainNode)
     91 {
     92     ASSERT(inherits(&s_info));
     93     const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name();
     94     putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
     95 }
     96 
     97 JSFunction::~JSFunction()
     98 {
     99     ASSERT(vptr() == JSGlobalData::jsFunctionVPtr);
    100 }
    101 
    102 static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function";
    103 static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function";
    104 
    105 static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message)
    106 {
    107     JSValue thrower = createTypeErrorFunction(exec, message);
    108     descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
    109 }
    110 
    111 const UString& JSFunction::name(ExecState* exec)
    112 {
    113     return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
    114 }
    115 
    116 const UString JSFunction::displayName(ExecState* exec)
    117 {
    118     JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName);
    119 
    120     if (displayName && isJSString(&exec->globalData(), displayName))
    121         return asString(displayName)->tryGetValue();
    122 
    123     return UString();
    124 }
    125 
    126 const UString JSFunction::calculatedDisplayName(ExecState* exec)
    127 {
    128     const UString explicitName = displayName(exec);
    129 
    130     if (!explicitName.isEmpty())
    131         return explicitName;
    132 
    133     return name(exec);
    134 }
    135 
    136 void JSFunction::markChildren(MarkStack& markStack)
    137 {
    138     Base::markChildren(markStack);
    139 
    140     markStack.append(&m_scopeChain);
    141     if (m_executable) {
    142         // Delightful race condition: m_executable may not have been initialised
    143         // if this is a host function, as the executable isn't necessarily created
    144         // until after the function has been allocated.
    145         markStack.append(&m_executable);
    146     }
    147 }
    148 
    149 CallType JSFunction::getCallData(CallData& callData)
    150 {
    151     if (isHostFunction()) {
    152         callData.native.function = nativeFunction();
    153         return CallTypeHost;
    154     }
    155     callData.js.functionExecutable = jsExecutable();
    156     callData.js.scopeChain = scope();
    157     return CallTypeJS;
    158 }
    159 
    160 JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&)
    161 {
    162     JSFunction* thisObj = asFunction(slotBase);
    163     ASSERT(!thisObj->isHostFunction());
    164     return exec->interpreter()->retrieveArguments(exec, thisObj);
    165 }
    166 
    167 JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&)
    168 {
    169     JSFunction* thisObj = asFunction(slotBase);
    170     ASSERT(!thisObj->isHostFunction());
    171     return exec->interpreter()->retrieveCaller(exec, thisObj);
    172 }
    173 
    174 JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&)
    175 {
    176     JSFunction* thisObj = asFunction(slotBase);
    177     ASSERT(!thisObj->isHostFunction());
    178     return jsNumber(thisObj->jsExecutable()->parameterCount());
    179 }
    180 
    181 bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
    182 {
    183     if (isHostFunction())
    184         return Base::getOwnPropertySlot(exec, propertyName, slot);
    185 
    186     if (propertyName == exec->propertyNames().prototype) {
    187         WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName);
    188 
    189         if (!location) {
    190             JSObject* prototype = constructEmptyObject(exec, scope()->globalObject->emptyObjectStructure());
    191             prototype->putDirect(exec->globalData(), exec->propertyNames().constructor, this, DontEnum);
    192             putDirect(exec->globalData(), exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
    193             location = getDirectLocation(exec->globalData(), propertyName);
    194         }
    195 
    196         slot.setValue(this, location->get(), offsetForLocation(location));
    197     }
    198 
    199     if (propertyName == exec->propertyNames().arguments) {
    200         if (jsExecutable()->isStrictMode()) {
    201             throwTypeError(exec, "Can't access arguments object of a strict mode function");
    202             slot.setValue(jsNull());
    203             return true;
    204         }
    205 
    206         slot.setCacheableCustom(this, argumentsGetter);
    207         return true;
    208     }
    209 
    210     if (propertyName == exec->propertyNames().length) {
    211         slot.setCacheableCustom(this, lengthGetter);
    212         return true;
    213     }
    214 
    215     if (propertyName == exec->propertyNames().caller) {
    216         if (jsExecutable()->isStrictMode()) {
    217             throwTypeError(exec, StrictModeCallerAccessError);
    218             slot.setValue(jsNull());
    219             return true;
    220         }
    221         slot.setCacheableCustom(this, callerGetter);
    222         return true;
    223     }
    224 
    225     return Base::getOwnPropertySlot(exec, propertyName, slot);
    226 }
    227 
    228 bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    229 {
    230     if (isHostFunction())
    231         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
    232 
    233     if (propertyName == exec->propertyNames().prototype) {
    234         PropertySlot slot;
    235         getOwnPropertySlot(exec, propertyName, slot);
    236         return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
    237     }
    238 
    239     if (propertyName == exec->propertyNames().arguments) {
    240         if (jsExecutable()->isStrictMode())
    241             createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
    242         else
    243             descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
    244         return true;
    245     }
    246 
    247     if (propertyName == exec->propertyNames().length) {
    248         descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
    249         return true;
    250     }
    251 
    252     if (propertyName == exec->propertyNames().caller) {
    253         if (jsExecutable()->isStrictMode())
    254             createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
    255         else
    256             descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
    257         return true;
    258     }
    259 
    260     return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
    261 }
    262 
    263 void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    264 {
    265     if (!isHostFunction() && (mode == IncludeDontEnumProperties)) {
    266         // Make sure prototype has been reified.
    267         PropertySlot slot;
    268         getOwnPropertySlot(exec, exec->propertyNames().prototype, slot);
    269 
    270         propertyNames.add(exec->propertyNames().arguments);
    271         propertyNames.add(exec->propertyNames().callee);
    272         propertyNames.add(exec->propertyNames().caller);
    273         propertyNames.add(exec->propertyNames().length);
    274     }
    275     Base::getOwnPropertyNames(exec, propertyNames, mode);
    276 }
    277 
    278 void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
    279 {
    280     if (isHostFunction()) {
    281         Base::put(exec, propertyName, value, slot);
    282         return;
    283     }
    284     if (propertyName == exec->propertyNames().prototype) {
    285         // Make sure prototype has been reified, such that it can only be overwritten
    286         // following the rules set out in ECMA-262 8.12.9.
    287         PropertySlot slot;
    288         getOwnPropertySlot(exec, propertyName, slot);
    289     }
    290     if (jsExecutable()->isStrictMode()) {
    291         if (propertyName == exec->propertyNames().arguments) {
    292             throwTypeError(exec, StrictModeArgumentsAccessError);
    293             return;
    294         }
    295         if (propertyName == exec->propertyNames().caller) {
    296             throwTypeError(exec, StrictModeCallerAccessError);
    297             return;
    298         }
    299     }
    300     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
    301         return;
    302     Base::put(exec, propertyName, value, slot);
    303 }
    304 
    305 bool JSFunction::deleteProperty(ExecState* exec, const Identifier& propertyName)
    306 {
    307     if (isHostFunction())
    308         return Base::deleteProperty(exec, propertyName);
    309     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
    310         return false;
    311     return Base::deleteProperty(exec, propertyName);
    312 }
    313 
    314 // ECMA 13.2.2 [[Construct]]
    315 ConstructType JSFunction::getConstructData(ConstructData& constructData)
    316 {
    317     if (isHostFunction())
    318         return ConstructTypeNone;
    319     constructData.js.functionExecutable = jsExecutable();
    320     constructData.js.scopeChain = scope();
    321     return ConstructTypeJS;
    322 }
    323 
    324 } // namespace JSC
    325