AST::Value CallValue(Context& context, AST::Value rawValue, HeapArray<AST::Value> args, const Debug::SourceLocation& location) { auto value = derefValue(std::move(rawValue)); if (getDerefType(value.type())->isTypename()) { return CallValue(context, GetStaticMethod(context, std::move(value), context.getCString("create"), location), std::move(args), location); } if (!value.type()->isCallable()) { // Try to use 'call' method. if (TypeCapabilities(context).hasCallMethod(getDerefType(value.type()))) { return CallValue(context, GetMethod(context, std::move(value), context.getCString("call"), location), std::move(args), location); } else { context.issueDiag(TypeNotCallableDiag(getDerefType(value.type())), location); return AST::Value::Constant(Constant::Integer(0), context.typeBuilder().getIntType()); } } const auto functionType = value.type()->asFunctionType(); const auto& typeList = functionType.parameterTypes(); if (functionType.attributes().isVarArg()) { if (args.size() < typeList.size()) { context.issueDiag(VarArgTooFewArgsDiag(value.toDiagString(), args.size(), typeList.size()), location); } } else { if (args.size() != typeList.size()) { context.issueDiag(CallIncorrectArgCountDiag(value.toDiagString(), args.size(), typeList.size()), location); } } if (!TypeCapabilities(context).isSized(functionType.returnType())) { // TODO: also check that the type is not abstract. context.issueDiag(CallReturnTypeIsUnsizedDiag(functionType.returnType()), location); } if (functionType.returnType()->hasConst()) { context.issueDiag(CallReturnTypeIsConstDiag(functionType.returnType()), location); } return addDebugInfo(AST::Value::Call(std::move(value), CastFunctionArguments(context, std::move(args), typeList, location), functionType.returnType()->stripConst()), location); }
bool ScriptInterpreter::execOpcode(byte opcode) { debugCN(kDebugScript, "opcode = %d (%s)\n", opcode, opcodeNames[opcode]); ScriptValue value1, value2, value3; uint32 temp; /* TODO: Put all opcodes into separate functions and into an array (but only after all needed opcodes are known and frozen) */ switch (opcode) { case opRet: return false; case opPush: loadValue(value1); derefValue(value1); push(value1); return true; case opPush0: push(ScriptValue(0)); return true; case opPush1: push(ScriptValue(1)); return true; case opPushNeg1: push(ScriptValue(-1)); return true; case opPop: loadValue(value1); pop(value2); copyValue(value1, value2); return true; case opMov: loadValue(value1); loadValue(value2); derefValue(value2); copyValue(value1, value2); return true; // Possibly join all jump variants into one opcode case opJmp: temp = _runningFunction->readUint32(); debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); return true; case opJl: temp = _runningFunction->readUint32(); if (_cmpFlags < 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJle: temp = _runningFunction->readUint32(); if (_cmpFlags <= 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJg: temp = _runningFunction->readUint32(); if (_cmpFlags > 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJge: temp = _runningFunction->readUint32(); if (_cmpFlags >= 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJz: temp = _runningFunction->readUint32(); if (_cmpFlags == 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJnz: temp = _runningFunction->readUint32(); if (_cmpFlags != 0) { debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); } return true; case opJmpByTable: temp = _runningFunction->readUint32(); debugCN(kDebugScript, "-> index = %d\n", _registers[0].value); _runningFunction->jumpRelative(_registers[0].value * 4); temp = _runningFunction->readUint32(); debugCN(kDebugScript, "-> ofs = %08X\n", temp); _runningFunction->jumpAbsolute(temp); return true; case opCmp: loadValue(value1); loadValue(value2); derefValue(value1); derefValue(value2); if (value1.type != kInteger || value2.type != kInteger) warning("ScriptInterpreter::execOpcode() Trying to compare non-integer values (%d, %d, line %d)", value1.type, value2.type, _lineNum); _cmpFlags = value1.value - value2.value; debugCN(kDebugScript, "-> cmp %d, %d\n", value1.value, value2.value); debugCN(kDebugScript, "-> _cmpFlags = %d\n", _cmpFlags); return true; case opCall: temp = _runningFunction->readUint32(); callFunction(temp); return true; case opCallKernel: temp = _runningFunction->readUint32(); callKernelFunction(temp); return true; case opInc: loadValue(value1); value2 = value1; derefValue(value2); value2.value++; copyValue(value1, value2); return true; case opDec: loadValue(value1); value2 = value1; derefValue(value2); value2.value--; copyValue(value1, value2); return true; case opAdd: loadValue(value1); value3 = value1; loadValue(value2); derefValue(value3); derefValue(value2); value3.value += value2.value; copyValue(value1, value3); return true; case opSub: loadValue(value1); value3 = value1; loadValue(value2); derefValue(value3); derefValue(value2); value3.value -= value2.value; copyValue(value1, value3); return true; case opDebug: _lineNum = (int)_runningFunction->readUint32(); return true; default: debugCN(kDebugScript, "Invalid opcode %d!\n", opcode); return false; } }