#include "CodeBlock.h"
#include "CommonIdentifiers.h"
#include "CallFrame.h"
+#include "ExceptionHelpers.h"
#include "FunctionPrototype.h"
#include "JSGlobalObject.h"
+#include "JSNotAnObject.h"
#include "Interpreter.h"
#include "ObjectPrototype.h"
#include "Parser.h"
using namespace Unicode;
namespace JSC {
+EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec)
+{
+ return throwVMError(exec, createNotAConstructorError(exec, exec->callee()));
+}
ASSERT_CLASS_FITS_IN_CELL(JSFunction);
-const ClassInfo JSFunction::info = { "Function", &InternalFunction::info, 0, 0 };
+const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0 };
bool JSFunction::isHostFunctionNonInline() const
{
return isHostFunction();
}
-JSFunction::JSFunction(NonNullPassRefPtr<Structure> structure)
- : Base(structure)
- , m_executable(adoptRef(new VPtrHackExecutable()))
+JSFunction::JSFunction(VPtrStealingHackType)
+ : Base(VPtrStealingHack)
{
}
-JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeExecutable* thunk, NativeFunction func)
- : Base(&exec->globalData(), structure, name)
-#if ENABLE(JIT)
- , m_executable(thunk)
-#endif
+JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeExecutable* thunk)
+ : Base(globalObject, structure)
+ , m_executable(exec->globalData(), this, thunk)
+ , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
{
-#if ENABLE(JIT)
- setNativeFunction(func);
- putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum);
-#else
- UNUSED_PARAM(thunk);
- UNUSED_PARAM(length);
- UNUSED_PARAM(func);
- ASSERT_NOT_REACHED();
-#endif
+ ASSERT(inherits(&s_info));
+ putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
+ putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
}
-JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func)
- : Base(&exec->globalData(), structure, name)
-#if ENABLE(JIT)
- , m_executable(exec->globalData().jitStubs->ctiNativeCallThunk())
-#endif
+JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeFunction func)
+ : Base(globalObject, structure)
+ , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain())
{
-#if ENABLE(JIT)
- setNativeFunction(func);
- putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum);
-#else
- UNUSED_PARAM(length);
- UNUSED_PARAM(func);
- ASSERT_NOT_REACHED();
-#endif
+ ASSERT(inherits(&s_info));
+
+ // Can't do this during initialization because getHostFunction might do a GC allocation.
+ m_executable.set(exec->globalData(), this, exec->globalData().getHostFunction(func));
+
+ putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
+ putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
}
-JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<FunctionExecutable> executable, ScopeChainNode* scopeChainNode)
- : Base(&exec->globalData(), exec->lexicalGlobalObject()->functionStructure(), executable->name())
- , m_executable(executable)
+JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
+ : Base(scopeChainNode->globalObject.get(), scopeChainNode->globalObject->functionStructure())
+ , m_executable(exec->globalData(), this, executable)
+ , m_scopeChain(exec->globalData(), this, scopeChainNode)
{
- setScopeChain(scopeChainNode);
+ ASSERT(inherits(&s_info));
+ const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name();
+ putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
}
JSFunction::~JSFunction()
{
ASSERT(vptr() == JSGlobalData::jsFunctionVPtr);
+}
- // JIT code for other functions may have had calls linked directly to the code for this function; these links
- // are based on a check for the this pointer value for this JSFunction - which will no longer be valid once
- // this memory is freed and may be reused (potentially for another, different JSFunction).
- if (!isHostFunction()) {
-#if ENABLE(JIT_OPTIMIZE_CALL)
- ASSERT(m_executable);
- if (jsExecutable()->isGenerated())
- jsExecutable()->generatedBytecode().unlinkCallers();
-#endif
- scopeChain().~ScopeChain(); // FIXME: Don't we need to do this in the interpreter too?
- }
+static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function";
+static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function";
+
+static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message)
+{
+ JSValue thrower = createTypeErrorFunction(exec, message);
+ descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter);
}
-void JSFunction::markChildren(MarkStack& markStack)
+const UString& JSFunction::name(ExecState* exec)
{
- Base::markChildren(markStack);
- if (!isHostFunction()) {
- jsExecutable()->markAggregate(markStack);
- scopeChain().markAggregate(markStack);
- }
+ return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
+}
+
+const UString JSFunction::displayName(ExecState* exec)
+{
+ JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName);
+
+ if (displayName && isJSString(&exec->globalData(), displayName))
+ return asString(displayName)->tryGetValue();
+
+ return UString();
+}
+
+const UString JSFunction::calculatedDisplayName(ExecState* exec)
+{
+ const UString explicitName = displayName(exec);
+
+ if (!explicitName.isEmpty())
+ return explicitName;
+
+ return name(exec);
+}
+
+void JSFunction::visitChildren(SlotVisitor& visitor)
+{
+ ASSERT_GC_OBJECT_INHERITS(this, &s_info);
+ COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
+ ASSERT(structure()->typeInfo().overridesVisitChildren());
+ Base::visitChildren(visitor);
+
+ visitor.append(&m_scopeChain);
+ if (m_executable)
+ visitor.append(&m_executable);
}
CallType JSFunction::getCallData(CallData& callData)
return CallTypeHost;
}
callData.js.functionExecutable = jsExecutable();
- callData.js.scopeChain = scopeChain().node();
+ callData.js.scopeChain = scope();
return CallTypeJS;
}
-JSValue JSFunction::call(ExecState* exec, JSValue thisValue, const ArgList& args)
-{
- ASSERT(!isHostFunction());
- return exec->interpreter()->execute(jsExecutable(), exec, this, thisValue.toThisObject(exec), args, scopeChain().node(), exec->exceptionSlot());
-}
-
JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&)
{
JSFunction* thisObj = asFunction(slotBase);
return exec->interpreter()->retrieveCaller(exec, thisObj);
}
-JSValue JSFunction::lengthGetter(ExecState* exec, JSValue slotBase, const Identifier&)
+JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&)
{
JSFunction* thisObj = asFunction(slotBase);
ASSERT(!thisObj->isHostFunction());
- return jsNumber(exec, thisObj->jsExecutable()->parameterCount());
+ return jsNumber(thisObj->jsExecutable()->parameterCount());
+}
+
+static inline WriteBarrierBase<Unknown>* createPrototypeProperty(JSGlobalData& globalData, JSGlobalObject* globalObject, JSFunction* function)
+{
+ ASSERT(!function->isHostFunction());
+
+ ExecState* exec = globalObject->globalExec();
+ if (WriteBarrierBase<Unknown>* location = function->getDirectLocation(globalData, exec->propertyNames().prototype))
+ return location;
+ JSObject* prototype = constructEmptyObject(exec, globalObject->emptyObjectStructure());
+ prototype->putDirect(globalData, exec->propertyNames().constructor, function, DontEnum);
+ function->putDirect(globalData, exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
+ return function->getDirectLocation(exec->globalData(), exec->propertyNames().prototype);
+}
+
+void JSFunction::preventExtensions(JSGlobalData& globalData)
+{
+ if (!isHostFunction())
+ createPrototypeProperty(globalData, scope()->globalObject.get(), this);
+ JSObject::preventExtensions(globalData);
}
bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
return Base::getOwnPropertySlot(exec, propertyName, slot);
if (propertyName == exec->propertyNames().prototype) {
- JSValue* location = getDirectLocation(propertyName);
+ WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName);
- if (!location) {
- JSObject* prototype = new (exec) JSObject(scopeChain().globalObject()->emptyObjectStructure());
- prototype->putDirect(exec->propertyNames().constructor, this, DontEnum);
- putDirect(exec->propertyNames().prototype, prototype, DontDelete);
- location = getDirectLocation(propertyName);
- }
+ if (!location)
+ location = createPrototypeProperty(exec->globalData(), scope()->globalObject.get(), this);
- slot.setValueSlot(this, location, offsetForLocation(location));
+ slot.setValue(this, location->get(), offsetForLocation(location));
}
if (propertyName == exec->propertyNames().arguments) {
+ if (jsExecutable()->isStrictMode()) {
+ throwTypeError(exec, "Can't access arguments object of a strict mode function");
+ slot.setValue(jsNull());
+ return true;
+ }
+
slot.setCacheableCustom(this, argumentsGetter);
return true;
}
}
if (propertyName == exec->propertyNames().caller) {
+ if (jsExecutable()->isStrictMode()) {
+ throwTypeError(exec, StrictModeCallerAccessError);
+ slot.setValue(jsNull());
+ return true;
+ }
slot.setCacheableCustom(this, callerGetter);
return true;
}
return Base::getOwnPropertySlot(exec, propertyName, slot);
}
- bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
- {
- if (isHostFunction())
- return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
-
- if (propertyName == exec->propertyNames().prototype) {
- PropertySlot slot;
- getOwnPropertySlot(exec, propertyName, slot);
- return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
- }
-
- if (propertyName == exec->propertyNames().arguments) {
+bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+{
+ if (isHostFunction())
+ return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
+
+ if (propertyName == exec->propertyNames().prototype) {
+ PropertySlot slot;
+ getOwnPropertySlot(exec, propertyName, slot);
+ return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
+ }
+
+ if (propertyName == exec->propertyNames().arguments) {
+ if (jsExecutable()->isStrictMode())
+ createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
+ else
descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
- return true;
- }
-
- if (propertyName == exec->propertyNames().length) {
- descriptor.setDescriptor(jsNumber(exec, jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
- return true;
- }
-
- if (propertyName == exec->propertyNames().caller) {
+ return true;
+ }
+
+ if (propertyName == exec->propertyNames().length) {
+ descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
+ return true;
+ }
+
+ if (propertyName == exec->propertyNames().caller) {
+ if (jsExecutable()->isStrictMode())
+ createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
+ else
descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
- return true;
- }
-
- return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
+ return true;
}
+ return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
+}
+
void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
if (!isHostFunction() && (mode == IncludeDontEnumProperties)) {
+ // Make sure prototype has been reified.
+ PropertySlot slot;
+ getOwnPropertySlot(exec, exec->propertyNames().prototype, slot);
+
propertyNames.add(exec->propertyNames().arguments);
propertyNames.add(exec->propertyNames().callee);
propertyNames.add(exec->propertyNames().caller);
Base::put(exec, propertyName, value, slot);
return;
}
+ if (propertyName == exec->propertyNames().prototype) {
+ // Make sure prototype has been reified, such that it can only be overwritten
+ // following the rules set out in ECMA-262 8.12.9.
+ PropertySlot slot;
+ getOwnPropertySlot(exec, propertyName, slot);
+ }
+ if (jsExecutable()->isStrictMode()) {
+ if (propertyName == exec->propertyNames().arguments) {
+ throwTypeError(exec, StrictModeArgumentsAccessError);
+ return;
+ }
+ if (propertyName == exec->propertyNames().caller) {
+ throwTypeError(exec, StrictModeCallerAccessError);
+ return;
+ }
+ }
if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
return;
Base::put(exec, propertyName, value, slot);
if (isHostFunction())
return ConstructTypeNone;
constructData.js.functionExecutable = jsExecutable();
- constructData.js.scopeChain = scopeChain().node();
+ constructData.js.scopeChain = scope();
return ConstructTypeJS;
}
-JSObject* JSFunction::construct(ExecState* exec, const ArgList& args)
-{
- ASSERT(!isHostFunction());
- Structure* structure;
- JSValue prototype = get(exec, exec->propertyNames().prototype);
- if (prototype.isObject())
- structure = asObject(prototype)->inheritorID();
- else
- structure = exec->lexicalGlobalObject()->emptyObjectStructure();
- JSObject* thisObj = new (exec) JSObject(structure);
-
- JSValue result = exec->interpreter()->execute(jsExecutable(), exec, this, thisObj, args, scopeChain().node(), exec->exceptionSlot());
- if (exec->hadException() || !result.isObject())
- return thisObj;
- return asObject(result);
-}
-
} // namespace JSC