Skip to content

Commit

Permalink
Update isCallable check function according to call operation
Browse files Browse the repository at this point in the history
* isFunction is replaced with isCallable check function
* call operations in Promise / TypedArray are fixed

Signed-off-by: HyukWoo Park <[email protected]>
  • Loading branch information
clover2123 committed Jun 19, 2019
1 parent 1dc3eb9 commit df62ef6
Show file tree
Hide file tree
Showing 38 changed files with 316 additions and 282 deletions.
7 changes: 6 additions & 1 deletion src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1292,7 +1292,7 @@ ValueRef* FunctionObjectRef::call(ExecutionStateRef* state, ValueRef* receiver,
for (size_t i = 0; i < argc; i++) {
newArgv[i] = toImpl(argv[i]);
}
return toRef(o->call(*toImpl(state), toImpl(receiver), argc, newArgv));
return toRef(Object::call(*toImpl(state), o, toImpl(receiver), argc, newArgv));
}

static void markEvalToCodeblock(InterpretedCodeBlock* cb)
Expand Down Expand Up @@ -1622,6 +1622,11 @@ bool ValueRef::isFunction() const
return Value(SmallValue::fromPayload(this)).isFunction();
}

bool ValueRef::isCallable() const
{
return Value(SmallValue::fromPayload(this)).isCallable();
}

bool ValueRef::toBoolean(ExecutionStateRef* es)
{
ExecutionState* esi = toImpl(es);
Expand Down
1 change: 1 addition & 0 deletions src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ class EXPORT ValueRef {
bool isString() const;
bool isObject() const;
bool isFunction() const;
bool isCallable() const;
bool isUndefinedOrNull() const
{
return isUndefined() || isNull();
Expand Down
22 changes: 12 additions & 10 deletions src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState& state, ByteCodeBlock* byteC
{
CallFunction* code = (CallFunction*)programCounter;
const Value& callee = registerFile[code->m_calleeIndex];
registerFile[code->m_resultIndex] = FunctionObject::call(state, callee, Value(), code->m_argumentCount, &registerFile[code->m_argumentsStartIndex]);
registerFile[code->m_resultIndex] = Object::call(state, callee, Value(), code->m_argumentCount, &registerFile[code->m_argumentsStartIndex]);
ADD_PROGRAM_COUNTER(CallFunction);
NEXT_INSTRUCTION();
}
Expand All @@ -569,7 +569,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState& state, ByteCodeBlock* byteC
CallFunctionWithReceiver* code = (CallFunctionWithReceiver*)programCounter;
const Value& callee = registerFile[code->m_calleeIndex];
const Value& receiver = registerFile[code->m_receiverIndex];
registerFile[code->m_resultIndex] = FunctionObject::call(state, callee, receiver, code->m_argumentCount, &registerFile[code->m_argumentsStartIndex]);
registerFile[code->m_resultIndex] = Object::call(state, callee, receiver, code->m_argumentCount, &registerFile[code->m_argumentsStartIndex]);
ADD_PROGRAM_COUNTER(CallFunctionWithReceiver);
NEXT_INSTRUCTION();
}
Expand Down Expand Up @@ -1268,7 +1268,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState& state, ByteCodeBlock* byteC
const Value& receiver = code->m_receiverIndex == REGISTER_LIMIT ? Value() : registerFile[code->m_receiverIndex];
ValueVector spreadArgs;
spreadFunctionArguments(state, &registerFile[code->m_argumentsStartIndex], code->m_argumentCount, spreadArgs);
registerFile[code->m_resultIndex] = FunctionObject::call(state, callee, receiver, spreadArgs.size(), spreadArgs.data());
registerFile[code->m_resultIndex] = Object::call(state, callee, receiver, spreadArgs.size(), spreadArgs.data());
ADD_PROGRAM_COUNTER(CallFunctionWithSpreadElement);
NEXT_INSTRUCTION();
}
Expand Down Expand Up @@ -1500,7 +1500,7 @@ NEVER_INLINE Object* ByteCodeInterpreter::newOperation(ExecutionState& state, co
if (callee.isConstructor() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_Not_Constructor);
}
return FunctionObject::construct(state, callee, argc, argv);
return Object::construct(state, callee, argc, argv);
}

// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-instanceofoperator
Expand All @@ -1517,7 +1517,7 @@ NEVER_INLINE Value ByteCodeInterpreter::instanceOfOperation(ExecutionState& stat
if (!instOfHandler.isUndefined()) {
// Return ToBoolean(Call(instOfHandler, C, «O»)).
Value arg[1] = { left };
return Value(FunctionObject::call(state, instOfHandler, right, 1, arg).toBoolean(state));
return Value(Object::call(state, instOfHandler, right, 1, arg).toBoolean(state));
}
#endif
// If IsCallable(C) is false, throw a TypeError exception.
Expand Down Expand Up @@ -2193,7 +2193,7 @@ NEVER_INLINE void ByteCodeInterpreter::evalOperation(ExecutionState& state, Call
thisValue = env->record()->asObjectEnvironmentRecord()->bindingObject();
}
}
registerFile[code->m_resultIndex] = FunctionObject::call(state, eval, thisValue, argc, argv);
registerFile[code->m_resultIndex] = Object::call(state, eval, thisValue, argc, argv);
}
}

Expand Down Expand Up @@ -2396,9 +2396,9 @@ NEVER_INLINE Value ByteCodeInterpreter::callFunctionInWithScope(ExecutionState&
if (code->m_hasSpreadElement) {
ValueVector spreadArgs;
spreadFunctionArguments(state, argv, code->m_argumentCount, spreadArgs);
return FunctionObject::call(state, callee, receiverObj, spreadArgs.size(), spreadArgs.data());
return Object::call(state, callee, receiverObj, spreadArgs.size(), spreadArgs.data());
}
return FunctionObject::call(state, callee, receiverObj, code->m_argumentCount, argv);
return Object::call(state, callee, receiverObj, code->m_argumentCount, argv);
}

void ByteCodeInterpreter::spreadFunctionArguments(ExecutionState& state, const Value* argv, const size_t argc, ValueVector& argVector)
Expand Down Expand Up @@ -2458,7 +2458,7 @@ Value ByteCodeInterpreter::yieldDelegateOperation(ExecutionState& state, Value*
return nextValue;
}

Value innerResult = FunctionObject::call(state, ret, iterator, 1, &nextValue);
Value innerResult = Object::call(state, ret, iterator, 1, &nextValue);

if (innerResult.isObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "IteratorResult is not an object");
Expand All @@ -2479,7 +2479,7 @@ Value ByteCodeInterpreter::yieldDelegateOperation(ExecutionState& state, Value*

if (throwMethod.isUndefined() == false) {
Value innerResult;
innerResult = FunctionObject::call(state, throwMethod, iterator, 1, &nextValue);
innerResult = Object::call(state, throwMethod, iterator, 1, &nextValue);
if (innerResult.isObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "IteratorResult is not an object");
}
Expand Down Expand Up @@ -2559,6 +2559,7 @@ NEVER_INLINE void ByteCodeInterpreter::declareFunctionDeclarations(ExecutionStat

NEVER_INLINE void ByteCodeInterpreter::defineObjectGetter(ExecutionState& state, ObjectDefineGetter* code, Value* registerFile)
{
// FIXME: FunctionObject
FunctionObject* fn = registerFile[code->m_objectPropertyValueRegisterIndex].asFunction();
String* pName = registerFile[code->m_objectPropertyNameRegisterIndex].toString(state);
StringBuilder builder;
Expand All @@ -2574,6 +2575,7 @@ NEVER_INLINE void ByteCodeInterpreter::defineObjectGetter(ExecutionState& state,

NEVER_INLINE void ByteCodeInterpreter::defineObjectSetter(ExecutionState& state, ObjectDefineSetter* code, Value* registerFile)
{
// FIXME: FunctionObject
FunctionObject* fn = registerFile[code->m_objectPropertyValueRegisterIndex].asFunction();
String* pName = registerFile[code->m_objectPropertyNameRegisterIndex].toString(state);
StringBuilder builder;
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/BoundFunctionObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Value BoundFunctionObject::call(ExecutionState& state, const Value& thisValue, c
}

// Return Call(target, boundThis, args).
return FunctionObject::call(state, m_boundTargetFunction, m_boundThis, mergedArgc, mergedArgv);
return Object::call(state, m_boundTargetFunction, m_boundThis, mergedArgc, mergedArgv);
}

// https://www.ecma-international.org/ecma-262/6.0/#sec-bound-function-exotic-objects-construct-argumentslist-newtarget
Expand All @@ -95,6 +95,6 @@ Object* BoundFunctionObject::construct(ExecutionState& state, const size_t calle
Value newConstructTarget = (Value(this) == Value(newTarget)) ? Value(m_boundTargetFunction) : newTarget;

// Return Construct(target, args, newTarget).
return FunctionObject::construct(state, m_boundTargetFunction, mergedArgc, mergedArgv, newConstructTarget);
return Object::construct(state, m_boundTargetFunction, mergedArgc, mergedArgv, newConstructTarget);
}
}
7 changes: 3 additions & 4 deletions src/runtime/BoundFunctionObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ class BoundFunctionObject : public Object {
return Value(m_boundTargetFunction).isConstructor();
}

virtual Value call(ExecutionState& state, const Value& thisValue, const size_t calledArgc, Value* calledArgv) override;

virtual Object* construct(ExecutionState& state, const size_t calledArgc, Value* calledArgv, const Value& newTarget) override;

Value targetFunction()
{
return m_boundTargetFunction;
Expand All @@ -62,6 +58,9 @@ class BoundFunctionObject : public Object {
}

private:
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t calledArgc, Value* calledArgv) override;
virtual Object* construct(ExecutionState& state, const size_t calledArgc, Value* calledArgv, const Value& newTarget) override;

SmallValue m_boundTargetFunction;
SmallValue m_boundThis;
SmallValueVector m_boundArguments;
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/FunctionObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ Value FunctionObject::processCall(ExecutionState& state, const Value& receiverSr
}

if (UNLIKELY(isSuperCall == true && isBuiltin() == true && isNewExpression == false)) {
Value returnValue = FunctionObject::construct(state, this, argc, argv);
Value returnValue = Object::construct(state, this, argc, argv);
returnValue.asObject()->setPrototype(state, receiverSrc.toObject(state)->getPrototype(state));
return returnValue;
}
Expand Down
44 changes: 10 additions & 34 deletions src/runtime/FunctionObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,40 +163,6 @@ class FunctionObject : public Object {
return FunctionKind::Normal;
}

// https://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, NULLABLE Value* argv) override
{
return processCall(state, thisValue, argc, argv, false);
}

virtual Object* construct(ExecutionState& state, const size_t argc, NULLABLE Value* argv, const Value& newTarget) override;

// https://www.ecma-international.org/ecma-262/6.0/#sec-call
ALWAYS_INLINE static Value call(ExecutionState& state, const Value& callee, const Value& thisValue, const size_t argc, NULLABLE Value* argv)
{
// If IsCallable(F) is false, throw a TypeError exception.
if (callee.isCallable() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_NOT_Callable);
}
// Return F.[[Call]](V, argumentsList).
return callee.asObject()->call(state, thisValue, argc, argv);
}

// https://www.ecma-international.org/ecma-262/6.0/#sec-construct
ALWAYS_INLINE static Object* construct(ExecutionState& state, const Value& constructor, const size_t argc, NULLABLE Value* argv, Value newTarget = Value(Value::EmptyValue))
{
// If newTarget was not passed, let newTarget be F.
if (newTarget.isEmpty() == true) {
newTarget = constructor;
}
// Assert: IsConstructor (F) is true.
ASSERT(constructor.isConstructor() == true);
// Assert: IsConstructor (newTarget) is true.
ASSERT(newTarget.isConstructor() == true);
// Return F.[[Construct]](argumentsList, newTarget).
return constructor.asObject()->construct(state, argc, argv, newTarget);
}

// http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2
virtual const char* internalClassProperty() override
{
Expand All @@ -209,6 +175,16 @@ class FunctionObject : public Object {
}

private:
friend class Object;
friend class ObjectGetResult;
// https://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, NULLABLE Value* argv) override
{
return processCall(state, thisValue, argc, argv, false);
}

virtual Object* construct(ExecutionState& state, const size_t argc, NULLABLE Value* argv, const Value& newTarget) override;

LexicalEnvironment* outerEnvironment()
{
return m_outerEnvironment;
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/GlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,11 +885,11 @@ static Value builtinDefineGetter(ExecutionState& state, Value thisValue, size_t
// Let O be ? ToObject(this value).
Object* O = thisValue.toObject(state);
// If IsCallable(getter) is false, throw a TypeError exception.
if (!argv[1].isFunction()) {
if (!argv[1].isCallable()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, String::emptyString, true, state.context()->staticStrings().__defineGetter__.string(), errorMessage_GlobalObject_CallbackNotCallable);
}
// Let desc be PropertyDescriptor{[[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true}.
ObjectPropertyDescriptor desc(JSGetterSetter(argv[1].asFunction(), Value(Value::EmptyValue)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));
ObjectPropertyDescriptor desc(JSGetterSetter(argv[1].asObject(), Value(Value::EmptyValue)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));

// Let key be ? ToPropertyKey(P).
ObjectPropertyName key(state, argv[0]);
Expand All @@ -910,11 +910,11 @@ static Value builtinDefineSetter(ExecutionState& state, Value thisValue, size_t
// Let O be ? ToObject(this value).
Object* O = thisValue.toObject(state);
// If IsCallable(getter) is false, throw a TypeError exception.
if (!argv[1].isFunction()) {
if (!argv[1].isCallable()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, String::emptyString, true, state.context()->staticStrings().__defineSetter__.string(), errorMessage_GlobalObject_CallbackNotCallable);
}
// Let desc be PropertyDescriptor{[[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true}.
ObjectPropertyDescriptor desc(JSGetterSetter(Value(Value::EmptyValue), argv[1].asFunction()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));
ObjectPropertyDescriptor desc(JSGetterSetter(Value(Value::EmptyValue), argv[1].asObject()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));

// Let key be ? ToPropertyKey(P).
ObjectPropertyName key(state, argv[0]);
Expand Down
Loading

0 comments on commit df62ef6

Please sign in to comment.