Optimized method invoke
This commit is contained in:
parent
d947e1aba3
commit
5df1469ba1
@ -95,6 +95,7 @@ typedef enum {
|
|||||||
EXPR_CAN_ASSIGN, /**< This expression may be an assignment target, check for assignment operators at the end. */
|
EXPR_CAN_ASSIGN, /**< This expression may be an assignment target, check for assignment operators at the end. */
|
||||||
EXPR_ASSIGN_TARGET, /**< This expression is definitely an assignment target or chained to one. */
|
EXPR_ASSIGN_TARGET, /**< This expression is definitely an assignment target or chained to one. */
|
||||||
EXPR_DEL_TARGET, /**< This expression is in the target list of a 'del' statement. */
|
EXPR_DEL_TARGET, /**< This expression is in the target list of a 'del' statement. */
|
||||||
|
EXPR_METHOD_CALL, /**< This expression is the parameter list of a method call; only used by @ref dot and @ref call */
|
||||||
} ExpressionType;
|
} ExpressionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -367,6 +368,7 @@ static KrkToken decorator(size_t level, FunctionType type);
|
|||||||
static void complexAssignment(ChunkRecorder before, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
static void complexAssignment(ChunkRecorder before, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
||||||
static void complexAssignmentTargets(KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
static void complexAssignmentTargets(KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
||||||
static int invalidTarget(int exprType, const char * description);
|
static int invalidTarget(int exprType, const char * description);
|
||||||
|
static void call(int exprType);
|
||||||
|
|
||||||
/* These are not the real parse functions. */
|
/* These are not the real parse functions. */
|
||||||
static void commaX(int exprType) { }
|
static void commaX(int exprType) { }
|
||||||
@ -1023,6 +1025,9 @@ _dotDone:
|
|||||||
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_SET_PROPERTY, ind);
|
||||||
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) {
|
} else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) {
|
||||||
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
||||||
|
} else if (match(TOKEN_LEFT_PAREN)) {
|
||||||
|
EMIT_OPERAND_OP(OP_GET_METHOD, ind);
|
||||||
|
call(EXPR_METHOD_CALL);
|
||||||
} else {
|
} else {
|
||||||
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
||||||
}
|
}
|
||||||
@ -3256,7 +3261,12 @@ static void call(int exprType) {
|
|||||||
*/
|
*/
|
||||||
argCount += 1 /* for the sentinel */ + 2 * specialArgs;
|
argCount += 1 /* for the sentinel */ + 2 * specialArgs;
|
||||||
}
|
}
|
||||||
EMIT_OPERAND_OP(OP_CALL, argCount);
|
|
||||||
|
if (exprType == EXPR_METHOD_CALL) {
|
||||||
|
EMIT_OPERAND_OP(OP_CALL_METHOD, argCount);
|
||||||
|
} else {
|
||||||
|
EMIT_OPERAND_OP(OP_CALL, argCount);
|
||||||
|
}
|
||||||
|
|
||||||
invalidTarget(exprType, "function call");
|
invalidTarget(exprType, "function call");
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,8 @@ typedef enum {
|
|||||||
OP_MAKE_SET,
|
OP_MAKE_SET,
|
||||||
OP_REVERSE,
|
OP_REVERSE,
|
||||||
OP_SLICE,
|
OP_SLICE,
|
||||||
|
OP_GET_METHOD,
|
||||||
|
OP_CALL_METHOD,
|
||||||
|
|
||||||
/* Two opcode instructions */
|
/* Two opcode instructions */
|
||||||
OP_JUMP_IF_FALSE_OR_POP,
|
OP_JUMP_IF_FALSE_OR_POP,
|
||||||
@ -148,6 +150,8 @@ typedef enum {
|
|||||||
OP_MAKE_SET_LONG,
|
OP_MAKE_SET_LONG,
|
||||||
OP_REVERSE_LONG,
|
OP_REVERSE_LONG,
|
||||||
OP_SLICE_LONG,
|
OP_SLICE_LONG,
|
||||||
|
OP_GET_METHOD_LONG,
|
||||||
|
OP_CALL_METHOD_LONG,
|
||||||
} KrkOpCode;
|
} KrkOpCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +58,7 @@ CONSTANT(OP_CLOSURE, CLOSURE_MORE)
|
|||||||
CONSTANT(OP_IMPORT, (void)0)
|
CONSTANT(OP_IMPORT, (void)0)
|
||||||
CONSTANT(OP_IMPORT_FROM, (void)0)
|
CONSTANT(OP_IMPORT_FROM, (void)0)
|
||||||
CONSTANT(OP_GET_SUPER, (void)0)
|
CONSTANT(OP_GET_SUPER, (void)0)
|
||||||
|
CONSTANT(OP_GET_METHOD, (void)0)
|
||||||
OPERAND(OP_KWARGS, (void)0)
|
OPERAND(OP_KWARGS, (void)0)
|
||||||
OPERAND(OP_SET_LOCAL, LOCAL_MORE)
|
OPERAND(OP_SET_LOCAL, LOCAL_MORE)
|
||||||
OPERAND(OP_GET_LOCAL, LOCAL_MORE)
|
OPERAND(OP_GET_LOCAL, LOCAL_MORE)
|
||||||
@ -76,6 +77,7 @@ OPERAND(OP_MAKE_DICT, (void)0)
|
|||||||
OPERAND(OP_MAKE_SET, (void)0)
|
OPERAND(OP_MAKE_SET, (void)0)
|
||||||
OPERAND(OP_REVERSE, (void)0)
|
OPERAND(OP_REVERSE, (void)0)
|
||||||
OPERAND(OP_SLICE, (void)0)
|
OPERAND(OP_SLICE, (void)0)
|
||||||
|
OPERAND(OP_CALL_METHOD, (void)0)
|
||||||
JUMP(OP_JUMP_IF_FALSE_OR_POP,+)
|
JUMP(OP_JUMP_IF_FALSE_OR_POP,+)
|
||||||
JUMP(OP_JUMP_IF_TRUE_OR_POP,+)
|
JUMP(OP_JUMP_IF_TRUE_OR_POP,+)
|
||||||
JUMP(OP_JUMP,+)
|
JUMP(OP_JUMP,+)
|
||||||
|
180
src/vm.c
180
src/vm.c
@ -584,7 +584,7 @@ int krk_processComplexArguments(int argCount, KrkValueArray * positionals, KrkTa
|
|||||||
* `extra` is passed by `callValue` to tell us which case we have, and thus
|
* `extra` is passed by `callValue` to tell us which case we have, and thus
|
||||||
* where we need to restore the stack to when we return from this call.
|
* where we need to restore the stack to when we return from this call.
|
||||||
*/
|
*/
|
||||||
static int call(KrkClosure * closure, int argCount, int callableOnStack) {
|
static int call(KrkClosure * closure, int argCount, int returnDepth) {
|
||||||
size_t potentialPositionalArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
size_t potentialPositionalArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
||||||
size_t totalArguments = closure->function->requiredArgs + closure->function->keywordArgs + !!(closure->function->flags & KRK_CODEOBJECT_FLAGS_COLLECTS_ARGS) + !!(closure->function->flags & KRK_CODEOBJECT_FLAGS_COLLECTS_KWS);
|
size_t totalArguments = closure->function->requiredArgs + closure->function->keywordArgs + !!(closure->function->flags & KRK_CODEOBJECT_FLAGS_COLLECTS_ARGS) + !!(closure->function->flags & KRK_CODEOBJECT_FLAGS_COLLECTS_KWS);
|
||||||
size_t offsetOfExtraArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
size_t offsetOfExtraArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
||||||
@ -726,7 +726,7 @@ _finishKwarg:
|
|||||||
|
|
||||||
if (unlikely(closure->function->flags & (KRK_CODEOBJECT_FLAGS_IS_GENERATOR | KRK_CODEOBJECT_FLAGS_IS_COROUTINE))) {
|
if (unlikely(closure->function->flags & (KRK_CODEOBJECT_FLAGS_IS_GENERATOR | KRK_CODEOBJECT_FLAGS_IS_COROUTINE))) {
|
||||||
KrkInstance * gen = krk_buildGenerator(closure, krk_currentThread.stackTop - argCount, argCount);
|
KrkInstance * gen = krk_buildGenerator(closure, krk_currentThread.stackTop - argCount, argCount);
|
||||||
krk_currentThread.stackTop = krk_currentThread.stackTop - argCount - callableOnStack;
|
krk_currentThread.stackTop = krk_currentThread.stackTop - argCount - returnDepth;
|
||||||
krk_push(OBJECT_VAL(gen));
|
krk_push(OBJECT_VAL(gen));
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@ -740,7 +740,7 @@ _finishKwarg:
|
|||||||
frame->closure = closure;
|
frame->closure = closure;
|
||||||
frame->ip = closure->function->chunk.code;
|
frame->ip = closure->function->chunk.code;
|
||||||
frame->slots = (krk_currentThread.stackTop - argCount) - krk_currentThread.stack;
|
frame->slots = (krk_currentThread.stackTop - argCount) - krk_currentThread.stack;
|
||||||
frame->outSlots = (krk_currentThread.stackTop - argCount - callableOnStack) - krk_currentThread.stack;
|
frame->outSlots = (krk_currentThread.stackTop - argCount - returnDepth) - krk_currentThread.stack;
|
||||||
frame->globals = &closure->function->globalsContext->fields;
|
frame->globals = &closure->function->globalsContext->fields;
|
||||||
FRAME_IN(frame);
|
FRAME_IN(frame);
|
||||||
return 1;
|
return 1;
|
||||||
@ -774,11 +774,11 @@ _errorAfterKeywords:
|
|||||||
* If callValue returns 0, the VM should already be in the exception state
|
* If callValue returns 0, the VM should already be in the exception state
|
||||||
* and it is not necessary to raise another exception.
|
* and it is not necessary to raise another exception.
|
||||||
*/
|
*/
|
||||||
int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
int krk_callValue(KrkValue callee, int argCount, int returnDepth) {
|
||||||
if (likely(IS_OBJECT(callee))) {
|
if (likely(IS_OBJECT(callee))) {
|
||||||
switch (OBJECT_TYPE(callee)) {
|
switch (OBJECT_TYPE(callee)) {
|
||||||
case KRK_OBJ_CLOSURE:
|
case KRK_OBJ_CLOSURE:
|
||||||
return call(AS_CLOSURE(callee), argCount, callableOnStack);
|
return call(AS_CLOSURE(callee), argCount, returnDepth);
|
||||||
case KRK_OBJ_NATIVE: {
|
case KRK_OBJ_NATIVE: {
|
||||||
NativeFn native = (NativeFn)AS_NATIVE(callee)->function;
|
NativeFn native = (NativeFn)AS_NATIVE(callee)->function;
|
||||||
if (unlikely(argCount && IS_KWARGS(krk_currentThread.stackTop[-1]))) {
|
if (unlikely(argCount && IS_KWARGS(krk_currentThread.stackTop[-1]))) {
|
||||||
@ -790,7 +790,7 @@ int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
argCount--; /* Because that popped the kwargs value */
|
argCount--; /* Because that popped the kwargs value */
|
||||||
krk_currentThread.stackTop -= argCount + callableOnStack; /* We can just put the stack back to normal */
|
krk_currentThread.stackTop -= argCount + returnDepth; /* We can just put the stack back to normal */
|
||||||
krk_push(myList);
|
krk_push(myList);
|
||||||
krk_push(myDict);
|
krk_push(myDict);
|
||||||
krk_currentThread.scratchSpace[0] = NONE_VAL();
|
krk_currentThread.scratchSpace[0] = NONE_VAL();
|
||||||
@ -814,7 +814,7 @@ int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
|||||||
free(stackCopy);
|
free(stackCopy);
|
||||||
}
|
}
|
||||||
if (unlikely(krk_currentThread.stackTop == krk_currentThread.stack)) return 0;
|
if (unlikely(krk_currentThread.stackTop == krk_currentThread.stack)) return 0;
|
||||||
krk_currentThread.stackTop -= argCount + callableOnStack;
|
krk_currentThread.stackTop -= argCount + returnDepth;
|
||||||
krk_push(result);
|
krk_push(result);
|
||||||
}
|
}
|
||||||
return 2;
|
return 2;
|
||||||
@ -822,7 +822,7 @@ int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
|||||||
case KRK_OBJ_INSTANCE: {
|
case KRK_OBJ_INSTANCE: {
|
||||||
KrkClass * _class = AS_INSTANCE(callee)->_class;
|
KrkClass * _class = AS_INSTANCE(callee)->_class;
|
||||||
if (likely(_class->_call != NULL)) {
|
if (likely(_class->_call != NULL)) {
|
||||||
return krk_callValue(OBJECT_VAL(_class->_call), argCount + 1, 0);
|
return krk_callValue(OBJECT_VAL(_class->_call), argCount + 1, returnDepth ? (returnDepth - 1) : 0);
|
||||||
} else {
|
} else {
|
||||||
krk_runtimeError(vm.exceptions->typeError, "'%s' object is not callable", krk_typeName(callee));
|
krk_runtimeError(vm.exceptions->typeError, "'%s' object is not callable", krk_typeName(callee));
|
||||||
return 0;
|
return 0;
|
||||||
@ -833,7 +833,7 @@ int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
|||||||
KrkInstance * newInstance = krk_newInstance(_class);
|
KrkInstance * newInstance = krk_newInstance(_class);
|
||||||
krk_currentThread.stackTop[-argCount - 1] = OBJECT_VAL(newInstance);
|
krk_currentThread.stackTop[-argCount - 1] = OBJECT_VAL(newInstance);
|
||||||
if (likely(_class->_init != NULL)) {
|
if (likely(_class->_init != NULL)) {
|
||||||
return krk_callValue(OBJECT_VAL(_class->_init), argCount + 1, 0);
|
return krk_callValue(OBJECT_VAL(_class->_init), argCount + 1, returnDepth ? (returnDepth - 1) : 0);
|
||||||
} else if (unlikely(argCount != 0)) {
|
} else if (unlikely(argCount != 0)) {
|
||||||
krk_runtimeError(vm.exceptions->typeError, "%s() takes no arguments (%d given)",
|
krk_runtimeError(vm.exceptions->typeError, "%s() takes no arguments (%d given)",
|
||||||
_class->name->chars, argCount);
|
_class->name->chars, argCount);
|
||||||
@ -848,7 +848,7 @@ int krk_callValue(KrkValue callee, int argCount, int callableOnStack) {
|
|||||||
krk_runtimeError(vm.exceptions->argumentError, "???");
|
krk_runtimeError(vm.exceptions->argumentError, "???");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return krk_callValue(OBJECT_VAL(bound->method), argCount + 1, 0);
|
return krk_callValue(OBJECT_VAL(bound->method), argCount + 1, returnDepth ? (returnDepth - 1): 0);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -926,6 +926,61 @@ int krk_bindMethod(KrkClass * _class, KrkString * name) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to @ref krk_bindMethod but does not create bound method objects.
|
||||||
|
* @returns 1 if the result was an (unbound) method, 2 if it was something else, 0 if it was not found.
|
||||||
|
*/
|
||||||
|
int krk_unbindMethod(KrkClass * _class, KrkString * name) {
|
||||||
|
KrkClass * originalClass = _class;
|
||||||
|
KrkValue method, out;
|
||||||
|
while (_class) {
|
||||||
|
if (krk_tableGet_fast(&_class->methods, name, &method)) break;
|
||||||
|
_class = _class->base;
|
||||||
|
}
|
||||||
|
if (!_class) return 0;
|
||||||
|
if (IS_NATIVE(method)) {
|
||||||
|
if (((KrkNative*)AS_OBJECT(method))->flags & KRK_NATIVE_FLAGS_IS_DYNAMIC_PROPERTY) {
|
||||||
|
out = AS_NATIVE(method)->function(1, (KrkValue[]){krk_peek(0)}, 0);
|
||||||
|
} else if (((KrkNative*)AS_OBJECT(method))->flags & KRK_NATIVE_FLAGS_IS_CLASS_METHOD) {
|
||||||
|
krk_pop(); /* the object */
|
||||||
|
krk_push(OBJECT_VAL(originalClass));
|
||||||
|
krk_push(method);
|
||||||
|
return 1;
|
||||||
|
} else if (((KrkNative*)AS_OBJECT(method))->flags & KRK_NATIVE_FLAGS_IS_STATIC_METHOD) {
|
||||||
|
out = method;
|
||||||
|
} else {
|
||||||
|
krk_push(method);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (IS_CLOSURE(method)) {
|
||||||
|
if (AS_CLOSURE(method)->flags & KRK_FUNCTION_FLAGS_IS_CLASS_METHOD) {
|
||||||
|
krk_pop();
|
||||||
|
krk_push(OBJECT_VAL(originalClass));
|
||||||
|
krk_push(method);
|
||||||
|
return 1;
|
||||||
|
} else if (AS_CLOSURE(method)->flags & KRK_FUNCTION_FLAGS_IS_STATIC_METHOD) {
|
||||||
|
out = method;
|
||||||
|
} else {
|
||||||
|
krk_push(method);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Does it have a descriptor __get__? */
|
||||||
|
KrkClass * type = krk_getType(method);
|
||||||
|
if (type->_descget) {
|
||||||
|
krk_push(krk_peek(0));
|
||||||
|
krk_push(method);
|
||||||
|
krk_swap(1);
|
||||||
|
krk_push(krk_callDirect(type->_descget, 2));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
out = method;
|
||||||
|
}
|
||||||
|
krk_push(out);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture upvalues and mark them as open. Called upon closure creation to
|
* Capture upvalues and mark them as open. Called upon closure creation to
|
||||||
* mark stack slots used by a function.
|
* mark stack slots used by a function.
|
||||||
@ -1384,11 +1439,16 @@ static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char *
|
|||||||
krk_push(OBJECT_VAL(methodName));
|
krk_push(OBJECT_VAL(methodName));
|
||||||
|
|
||||||
/* Bind from a */
|
/* Bind from a */
|
||||||
|
int res;
|
||||||
KrkClass * type = krk_getType(a);
|
KrkClass * type = krk_getType(a);
|
||||||
krk_push(a);
|
krk_push(a);
|
||||||
if (krk_bindMethod(type, methodName)) {
|
if ((res = krk_unbindMethod(type, methodName))) {
|
||||||
|
krk_swap(1);
|
||||||
|
if (res == 2) {
|
||||||
|
krk_pop();
|
||||||
|
}
|
||||||
krk_push(b);
|
krk_push(b);
|
||||||
value = krk_callStack(1);
|
value = krk_callStack(res == 2 ? 1 : 2);
|
||||||
if (!IS_NOTIMPL(value)) goto _success;
|
if (!IS_NOTIMPL(value)) goto _success;
|
||||||
krk_pop(); /* name */
|
krk_pop(); /* name */
|
||||||
} else {
|
} else {
|
||||||
@ -1401,9 +1461,13 @@ static KrkValue tryBind(const char * name, KrkValue a, KrkValue b, const char *
|
|||||||
krk_push(OBJECT_VAL(methodName));
|
krk_push(OBJECT_VAL(methodName));
|
||||||
type = krk_getType(b);
|
type = krk_getType(b);
|
||||||
krk_push(b);
|
krk_push(b);
|
||||||
if (krk_bindMethod(type, methodName)) {
|
if ((res = krk_unbindMethod(type, methodName))) {
|
||||||
|
krk_swap(1);
|
||||||
|
if (res == 2) {
|
||||||
|
krk_pop();
|
||||||
|
}
|
||||||
krk_push(a);
|
krk_push(a);
|
||||||
value = krk_callStack(1);
|
value = krk_callStack(res == 2 ? 1 : 2);
|
||||||
if (!IS_NOTIMPL(value)) goto _success;
|
if (!IS_NOTIMPL(value)) goto _success;
|
||||||
krk_pop(); /* name */
|
krk_pop(); /* name */
|
||||||
} else {
|
} else {
|
||||||
@ -1907,6 +1971,64 @@ static int valueGetProperty(KrkString * name) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to the above, but specifically for getting properties that will be
|
||||||
|
* immediately called, eg. for GET_METHOD that is followed by argument pushing
|
||||||
|
* and then CALL_METHOD.
|
||||||
|
*/
|
||||||
|
static int valueGetMethod(KrkString * name) {
|
||||||
|
KrkValue this = krk_peek(0);
|
||||||
|
KrkClass * objectClass;
|
||||||
|
KrkValue value;
|
||||||
|
if (IS_INSTANCE(this)) {
|
||||||
|
KrkInstance * instance = AS_INSTANCE(this);
|
||||||
|
if (krk_tableGet_fast(&instance->fields, name, &value)) {
|
||||||
|
krk_push(value);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
objectClass = instance->_class;
|
||||||
|
} else if (IS_CLASS(this)) {
|
||||||
|
KrkClass * _class = AS_CLASS(this);
|
||||||
|
do {
|
||||||
|
if (krk_tableGet_fast(&_class->methods, name, &value)) {
|
||||||
|
if ((IS_CLOSURE(value) && (AS_CLOSURE(value)->flags & KRK_FUNCTION_FLAGS_IS_CLASS_METHOD)) ||
|
||||||
|
(IS_NATIVE(value) && (AS_NATIVE(value)->flags & KRK_NATIVE_FLAGS_IS_CLASS_METHOD))) {
|
||||||
|
krk_push(value);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
krk_push(value);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
_class = _class->base;
|
||||||
|
} while (_class);
|
||||||
|
objectClass = vm.baseClasses->typeClass;
|
||||||
|
} else if (IS_CLOSURE(krk_peek(0))) {
|
||||||
|
KrkClosure * closure = AS_CLOSURE(this);
|
||||||
|
if (krk_tableGet_fast(&closure->fields, name, &value)) {
|
||||||
|
krk_push(value);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
objectClass = vm.baseClasses->functionClass;
|
||||||
|
} else {
|
||||||
|
objectClass = krk_getType(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if the base class for this non-instance type has a method available */
|
||||||
|
int maybe = krk_unbindMethod(objectClass, name);
|
||||||
|
if (maybe) return maybe;
|
||||||
|
|
||||||
|
if (objectClass->_getattr) {
|
||||||
|
krk_push(krk_peek(0));
|
||||||
|
krk_push(OBJECT_VAL(name));
|
||||||
|
krk_push(krk_callDirect(objectClass->_getattr, 2));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
KrkValue krk_valueGetAttribute(KrkValue value, char * name) {
|
KrkValue krk_valueGetAttribute(KrkValue value, char * name) {
|
||||||
krk_push(OBJECT_VAL(krk_copyString(name,strlen(name))));
|
krk_push(OBJECT_VAL(krk_copyString(name,strlen(name))));
|
||||||
krk_push(value);
|
krk_push(value);
|
||||||
@ -2576,6 +2698,18 @@ _finishReturn: (void)0;
|
|||||||
frame = &krk_currentThread.frames[krk_currentThread.frameCount - 1];
|
frame = &krk_currentThread.frames[krk_currentThread.frameCount - 1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CALL_METHOD_LONG:
|
||||||
|
THREE_BYTE_OPERAND;
|
||||||
|
case OP_CALL_METHOD: {
|
||||||
|
ONE_BYTE_OPERAND;
|
||||||
|
if (IS_NONE(krk_peek(OPERAND+1))) {
|
||||||
|
if (unlikely(!krk_callValue(krk_peek(OPERAND), OPERAND, 2))) goto _finishException;
|
||||||
|
} else {
|
||||||
|
if (unlikely(!krk_callValue(krk_peek(OPERAND+1), OPERAND+1, 1))) goto _finishException;
|
||||||
|
}
|
||||||
|
frame = &krk_currentThread.frames[krk_currentThread.frameCount - 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_EXPAND_ARGS_LONG:
|
case OP_EXPAND_ARGS_LONG:
|
||||||
THREE_BYTE_OPERAND;
|
THREE_BYTE_OPERAND;
|
||||||
case OP_EXPAND_ARGS: {
|
case OP_EXPAND_ARGS: {
|
||||||
@ -2737,6 +2871,24 @@ _finishReturn: (void)0;
|
|||||||
krk_pop();
|
krk_pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_GET_METHOD_LONG:
|
||||||
|
THREE_BYTE_OPERAND;
|
||||||
|
case OP_GET_METHOD: {
|
||||||
|
ONE_BYTE_OPERAND;
|
||||||
|
KrkString * name = READ_STRING(OPERAND);
|
||||||
|
int result = valueGetMethod(name);
|
||||||
|
if (result == 2) {
|
||||||
|
krk_push(NONE_VAL());
|
||||||
|
krk_swap(2);
|
||||||
|
krk_pop();
|
||||||
|
} else if (unlikely(!result)) {
|
||||||
|
krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars);
|
||||||
|
goto _finishException;
|
||||||
|
} else {
|
||||||
|
krk_swap(1); /* unbound-method object */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_DUP_LONG:
|
case OP_DUP_LONG:
|
||||||
THREE_BYTE_OPERAND;
|
THREE_BYTE_OPERAND;
|
||||||
case OP_DUP:
|
case OP_DUP:
|
||||||
|
Loading…
Reference in New Issue
Block a user