diff --git a/src/checker/check2.h b/src/checker/check2.h index bd0e64d..13ae2e7 100644 --- a/src/checker/check2.h +++ b/src/checker/check2.h @@ -124,12 +124,35 @@ namespace ts::vm2 { return false; } case TypeKind::Union: { - auto current = (TypeRef *) right->type; - while (current) { - if (extends(left, current->type)) return true; - current = current->next; + if (left->kind == TypeKind::Union) { + //number | string extends number => false + //number | string extends number | string => true + //string extends number | string => true + + //pretty inefficient I know + auto current = (TypeRef *) right->type; + while (current) { + auto currentLeft = (TypeRef *) left->type; + bool fit = false; + while (currentLeft) { + if (extends(currentLeft->type, current->type)) { + fit = true; + break; + }; + currentLeft = currentLeft->next; + } + if (!fit) return false; + current = current->next; + } + return true; + } else { + auto current = (TypeRef *) right->type; + while (current) { + if (extends(left, current->type)) return true; + current = current->next; + } + return false; } - return false; } case TypeKind::Literal: { switch (left->kind) { diff --git a/src/checker/compiler.h b/src/checker/compiler.h index 76e920c..034eed9 100644 --- a/src/checker/compiler.h +++ b/src/checker/compiler.h @@ -271,10 +271,10 @@ namespace ts::checker { for (unsigned int i = 0; visit.active && ipopSection(); } + //if > 0 expression statements keep the return value on the stack, otherwise they are removed. + // e.g. `doIt()` removes its return type of `doIt()` while `doIt().deep()` keeps it (so that deep can be resolved). + unsigned int expressionResult = 0; + void pushKeepExpressionResult() { + expressionResult++; + } + void popKeepExpressionResult() { + expressionResult--; + } void pushSlots() { currentSubroutine()->slotIP = ip(); pushOp(OP::Slots); @@ -779,31 +788,80 @@ namespace ts::checker { } } - void pushFunction(OP op, sharedOpt node, Program &program, sharedOpt withName) { - if (node->typeParameters) { + void pushFunction(OP op, sharedOpt node, Program &program, sharedOpt withName) { + sharedOpt body; + sharedOpt type; + sharedOpt typeParameters; + sharedOpt parameters; + if (auto a = to(node)) { + body = a->body; + type = a->type; + parameters = a->parameters; + typeParameters = a->typeParameters; + } else if (auto a = to(node)) { + body = a->body; + type = a->type; + parameters = a->parameters; + typeParameters = a->typeParameters; + } else { + throw std::runtime_error("function type not supported"); + } + + auto pushBodyType = [&] { + unsigned int bodyAddress; + if (body) { + bodyAddress = program.pushSubroutineNameLess(); + program.pushOp(OP::TypeArgument); + handle(body, program); + program.pushOp(OP::Loads); + program.pushUint16(0); + program.pushOp(OP::UnwrapInferBody); + program.popSubroutine(); + } + + if (type) { + handle(type, program); + + if (bodyAddress) { + //type and body given, so we check if all `return` are valid. + //it is moved in its own subroutine, so that the return type is cached and only run once. + auto checkBodyAddress = program.pushSubroutineNameLess(); + program.pushOp(OP::CheckBody); + program.pushAddress(bodyAddress); + program.popSubroutine(); + + program.pushOp(OP::Call); + program.pushAddress(checkBodyAddress); + program.pushUint16(0); + } + } else { + if (bodyAddress) { + //no type given, so we infer from body + program.pushOp(OP::InferBody); + program.pushAddress(bodyAddress); + } else { + program.pushOp(OP::Unknown); + } + } + }; + + if (typeParameters) { //when there are type parameters, FunctionDeclaration returns a FunctionRef //which indicates the VM that the function needs to be instantiated first. auto subroutineIndex = program.pushSubroutineNameLess(); unsigned int size = 0; - for (auto &¶m: node->typeParameters->list) { + for (auto &¶m: typeParameters->list) { handle(param, program); } program.pushSlots(); //first comes the return type size++; - if (node->type) { - handle(node->type, program); - } else { - //todo: Infer from body, put into own subroutine so it is cached - program.pushOp(OP::Unknown); - if (node->body) { - } else { - } - } - for (auto &¶m: node->parameters->list) { + pushBodyType(); + + for (auto &¶m: parameters->list) { size++; handle(param, program); } @@ -821,17 +879,10 @@ namespace ts::checker { unsigned int size = 0; //first comes the return type size++; - if (node->type) { - handle(node->type, program); - } else { - //todo: Infer from body - program.pushOp(OP::Unknown); - if (node->body) { - } else { - } - } - for (auto &¶m: node->parameters->list) { + pushBodyType(); + + for (auto &¶m: parameters->list) { size++; handle(param, program); } @@ -1179,6 +1230,15 @@ namespace ts::checker { program.pushUint16(size); break; } + case SyntaxKind::IfStatement: { + const auto n = to(node); + //todo handle n->expression, necessary for type narrowing + handle(n->thenStatement, program); + if (n->elseStatement) { + handle(n->elseStatement, program); + } + break; + } case SyntaxKind::ParenthesizedExpression: { const auto n = to(node); handle(n->expression, program); @@ -1232,9 +1292,27 @@ namespace ts::checker { break; } + case SyntaxKind::ReturnStatement: { + const auto n = to(node); + handle(n->expression, program); + program.pushOp(OP::ReturnStatement); + break; + } + case SyntaxKind::Block: { + const auto n = to(node); + for (auto &&statement: n->statements->list) { + handle(statement, program); + } + break; + } case SyntaxKind::ExpressionStatement: { const auto n = to(node); handle(n->expression, program); + if (!program.expressionResult) { + //expression statements that are not used need to be removed from the stack. + //.e.g `true;` or `doIt();` + program.pushOp(OP::Pop); + } break; } case SyntaxKind::ConditionalExpression: { @@ -1355,11 +1433,13 @@ namespace ts::checker { } case SyntaxKind::PropertyAccessExpression: { //e.g. object.method + program.pushKeepExpressionResult(); auto n = to(node); handle(n->expression, program); //to avoid resolving Identifier to a symbol, we handle it manually pushName(n->name, program); program.pushOp(OP::PropertyAccess, node); + program.popKeepExpressionResult(); break; } case SyntaxKind::ClassExpression: { diff --git a/src/checker/debug.h b/src/checker/debug.h index ca588fe..933eb2d 100644 --- a/src/checker/debug.h +++ b/src/checker/debug.h @@ -141,7 +141,10 @@ namespace ts::checker { newLine = true; break; } + case OP::CheckBody: + case OP::InferBody: case OP::SelfCheck: + case OP::Inline: case OP::Set: case OP::TypeArgumentDefault: { params += fmt::format(" &{}", vm::readUint32(bin, i + 1)); diff --git a/src/checker/instructions.h b/src/checker/instructions.h index 446f9f0..7bdcf81 100644 --- a/src/checker/instructions.h +++ b/src/checker/instructions.h @@ -127,15 +127,21 @@ namespace ts::instructions { Dup, //Duplicates the current stack end Set, //narrows/Sets a new value for a subroutine (variables) Error, + Pop, + Inline, //Execute a subroutine on the same active frame - Frame, //creates a new stack frame - FrameEnd, - Return, + //Frame, //creates a new stack frame + //FrameEnd, + Return, //end of a subroutine + ReturnStatement, //used in inferring return types of functions Subroutine, Distribute, //calls a subroutine for each union member. one parameter (address to subroutine) Call, //call a subroutine and push the result on the stack TailCall, + CheckBody, + InferBody, + UnwrapInferBody, }; enum class ErrorCode { diff --git a/src/checker/module2.h b/src/checker/module2.h index 4fc484b..ac74f7b 100644 --- a/src/checker/module2.h +++ b/src/checker/module2.h @@ -142,15 +142,15 @@ namespace ts::vm2 { auto map = findNormalizedMap(e.ip); if (map.found()) { - std::size_t lineStart = code.rfind('\n', map.pos); + auto lineStart = code.rfind('\n', map.pos); lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; - std::size_t lineEnd = code.find('\n', map.end); + auto lineEnd = code.find('\n', map.end); if (lineEnd == std::string::npos) lineEnd = code.size(); std::cout << cyan << fileName << ":" << yellow << map.pos << ":" << map.end << reset << " - " << red << "error" << reset << " TS0000: " << e.message << "\n\n"; - std::cout << code.substr(lineStart, lineEnd - lineStart - 1) << "\n"; + std::cout << "»" << code.substr(lineStart, lineEnd - lineStart - 1) << "\n"; auto space = map.pos - lineStart; - std::cout << std::string(space, ' ') << red << "^" << reset << "\n\n"; + std::cout << "»" << std::string(space, ' ') << red << std::string(map.end - map.pos, '~') << reset << "\n\n"; continue; } } diff --git a/src/checker/types2.h b/src/checker/types2.h index d11bb32..80ae24f 100644 --- a/src/checker/types2.h +++ b/src/checker/types2.h @@ -44,18 +44,18 @@ namespace ts::vm2 { //Used in the vm enum TypeFlag: unsigned int { - Readonly = 1 << 0, - Optional = 1 << 1, - StringLiteral = 1 << 2, - NumberLiteral = 1 << 3, - BooleanLiteral = 1 << 4, - BigIntLiteral = 1 << 5, - True = 1 << 6, - False = 1 << 7, - Stored = 1 << 8, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified - RestReuse = 1 << 9, //allow to reuse/steal T in ...T - Deleted = 1 << 10, //for debugging purposes - Static = 1 << 11, + Readonly = 1<<0, + Optional = 1<<1, + StringLiteral = 1<<2, + NumberLiteral = 1<<3, + BooleanLiteral = 1<<4, + BigIntLiteral = 1<<5, + True = 1<<6, + False = 1<<7, + Stored = 1<<8, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified + RestReuse = 1<<9, //allow to reuse/steal T in ...T + Deleted = 1<<10, //for debugging purposes + Static = 1<<11, }; struct Type; @@ -93,7 +93,7 @@ namespace ts::vm2 { // if (extended) { // extended->push_back(type); // } else { - if (p > defaultTypesSize) { + if (p>defaultTypesSize) { // createExtended(300); // extended = new std::vector(32); // extended->insert(extended->begin(), list.begin(), list.end()); @@ -143,6 +143,10 @@ namespace ts::vm2 { if (kind == TypeKind::Literal && type) delete (string *) type; }; + bool isDeleted() { + return flag & TypeFlag::Deleted; + } + void fromLiteral(Type *literal) { flag = literal->flag; if (literal->type) { @@ -216,6 +220,7 @@ namespace ts::vm2 { } else { ((TypeRef *) type)->next = ref; } + size++; } void readStorage(const string_view &bin, uint32_t offset) { @@ -226,6 +231,15 @@ namespace ts::vm2 { } }; + inline void forEach(Type *type, std::function callback) { + bool stop = false; + + auto current = (TypeRef *) type->type; + while (!stop && current) { + callback(current, stop); + } + } + inline void stringifyType(Type *type, std::string &r) { switch (type->kind) { case TypeKind::Boolean: { @@ -262,7 +276,7 @@ namespace ts::vm2 { unsigned int i = 0; auto current = (TypeRef *) type->type; while (current) { - if (i++ > 20) { + if (i++>20) { r += "..."; break; } @@ -307,7 +321,7 @@ namespace ts::vm2 { auto current = (TypeRef *) type->type; unsigned int i = 0; while (current) { - if (i++ > 20) { + if (i++>20) { r += "..."; break; } @@ -322,7 +336,7 @@ namespace ts::vm2 { auto current = (TypeRef *) type->type; unsigned int i = 0; while (current) { - if (i++ > 20) { + if (i++>20) { r += "..."; break; } diff --git a/src/checker/utils.h b/src/checker/utils.h index 399346c..e6cc0b1 100644 --- a/src/checker/utils.h +++ b/src/checker/utils.h @@ -90,7 +90,10 @@ namespace ts::vm { break; } case OP::Set: + case OP::CheckBody: + case OP::InferBody: case OP::SelfCheck: + case OP::Inline: case OP::TypeArgumentDefault: { *i += 4; break; diff --git a/src/checker/vm2.cpp b/src/checker/vm2.cpp index 4dd0ca3..9c15fd5 100644 --- a/src/checker/vm2.cpp +++ b/src/checker/vm2.cpp @@ -2,14 +2,13 @@ #include "../hash.h" #include "./check2.h" #include "./vm2_utils.h" -#include namespace ts::vm2 { void prepare(shared &module) { parseHeader(module); - activeSubroutine->module = module.get(); - activeSubroutine->ip = module->subroutines[0].address; //first is main - activeSubroutine->depth = 0; + subroutine->module = module.get(); + subroutine->ip = module->subroutines[0].address; //first is main + subroutine->depth = 0; } // TypeRef is an owning reference @@ -48,19 +47,104 @@ namespace ts::vm2 { gcQueue[gcQueueIdx++] = type; } - inline Type *widen(Type *type) { + //Used in the vm + enum TypeWidenFlag: unsigned int { + Any = 1<<1, + String = 1<<2, + Number = 1<<3, + Boolean = 1<<4, + BigInt = 1<<5, + }; + + inline Type *_widen(Type *type) { + //see https://github.com/Microsoft/TypeScript/pull/10676 switch (type->kind) { case TypeKind::Literal: { if (type->flag & TypeFlag::StringLiteral) return allocate(TypeKind::String); if (type->flag & TypeFlag::NumberLiteral) return allocate(TypeKind::Number); if (type->flag & TypeFlag::BooleanLiteral) return allocate(TypeKind::Boolean); if (type->flag & TypeFlag::BigIntLiteral) return allocate(TypeKind::BigInt); + throw std::runtime_error("Invalid literal to widen"); + } + case TypeKind::Union: { + auto current = (TypeRef *) type->type; + bool found = false; + while (!found && current) { + switch (current->type->kind) { + case TypeKind::Null: + case TypeKind::Undefined: + case TypeKind::Literal: { + found = true; + break; + } + } + current = current->next; + } + + if (found) { + //we found something to widen + auto current = (TypeRef *) type->type; + //widen each literal in the union. + //remove duplicates (like "12" | "23" => string | string => string) + unsigned int flag = 0; + + auto newUnion = allocate(TypeKind::Union); + + while (current) { + switch (current->type->kind) { + case TypeKind::Null: + case TypeKind::Undefined: { + if (!(flag & TypeWidenFlag::Any)) { + newUnion->appendChild(useAsRef(allocate(TypeKind::Any))); + flag |= TypeWidenFlag::Any; + } + break; + } + case TypeKind::Literal: { + if (current->type->flag & TypeFlag::StringLiteral && !(flag & TypeWidenFlag::String)) { + newUnion->appendChild(useAsRef(allocate(TypeKind::String))); + flag |= TypeWidenFlag::String; + } + if (current->type->flag & TypeFlag::NumberLiteral && !(flag & TypeWidenFlag::Number)) { + newUnion->appendChild(useAsRef(allocate(TypeKind::Number))); + flag |= TypeWidenFlag::Number; + } + if (current->type->flag & TypeFlag::BooleanLiteral && !(flag & TypeWidenFlag::Boolean)) { + newUnion->appendChild(useAsRef(allocate(TypeKind::Boolean))); + flag |= TypeWidenFlag::Boolean; + } + if (current->type->flag & TypeFlag::BigIntLiteral && !(flag & TypeWidenFlag::BigInt)) { + newUnion->appendChild(useAsRef(allocate(TypeKind::BigInt))); + flag |= TypeWidenFlag::BigInt; + } + break; + } + default: { + newUnion->appendChild(useAsRef(current->type)); + break; + }; + } + current = current->next; + } + return newUnion; + } + break; } } return type; } + inline Type *widen(Type *type) { + auto widened = _widen(type); + if (type == widened) { + return type; + } else { + gc(type); + return widened; + } + } + void gc(Type *type) { //debug("gc refCount={} {} ref={}", type->refCount, stringify(type), (void *) type); if (type->refCount>0) return; @@ -171,27 +255,27 @@ namespace ts::vm2 { inline void popFrameWithoutGC() { throw std::runtime_error("deprecated"); - //sp = frame->initialSp; + //sp = subroutine->initialSp; //frame = frames.pop(); } inline std::span popFrame() { throw std::runtime_error("deprecated"); - //auto start = frame->initialSp + frame->variables; + //auto start = subroutine->initialSp + subroutine->variables; //std::span sub{stack.data() + start, sp - start}; - //if (frame->variables>0) { + //if (subroutine->variables>0) { // //we need to GC all variables - // for (unsigned int i = 0; ivariables; i++) { - // gc(stack[frame->initialSp + i]); + // for (unsigned int i = 0; ivariables; i++) { + // gc(stack[subroutine->initialSp + i]); // } //} - //sp = frame->initialSp; + //sp = subroutine->initialSp; //frame = frames.pop(); //&frames[--frameIdx]; //return sub; } inline void report(DiagnosticMessage message) { - message.module = activeSubroutine->module; + message.module = subroutine->module; message.module->errors.push_back(message); } @@ -200,7 +284,7 @@ namespace ts::vm2 { } inline void report(const string &message) { - report(DiagnosticMessage(message, activeSubroutine->ip)); + report(DiagnosticMessage(message, subroutine->ip)); } inline void report(const string &message, unsigned int ip) { @@ -211,7 +295,7 @@ namespace ts::vm2 { // auto next = frames.push(); ///&frames[frameIdx++]; // //important to reset necessary stuff, since we reuse // next->initialSp = sp; -// next->depth = frame->depth + 1; +// next->depth = subroutine->depth + 1; //// debug("pushFrame {}", sp); // next->variables = 0; // frame = next; @@ -219,7 +303,7 @@ namespace ts::vm2 { //Returns true if it actually jumped to another subroutine, false if it just pushed its cached type. inline bool tailCall(unsigned int address, unsigned int arguments) { - auto routine = activeSubroutine->module->getSubroutine(address); + auto routine = subroutine->module->getSubroutine(address); if (routine->narrowed) { push(routine->narrowed); return false; @@ -235,9 +319,9 @@ namespace ts::vm2 { } ////remove all open frames - //while (frame->depth>0) { - // for (unsigned int i = 0; ivariables; i++) { - // drop(stack[frame->initialSp + i]); + //while (subroutine->depth>0) { + // for (unsigned int i = 0; ivariables; i++) { + // drop(stack[subroutine->initialSp + i]); // } // frame = frames.pop(); //} @@ -245,8 +329,8 @@ namespace ts::vm2 { //stack could look like that: // | [T] [T] [V] [P1] [P2] [TailCall] | // T=TypeArgument, V=TypeVariable, but we do not need anything of that, so we GC that. P indicates argument for the call. - for (unsigned int i = 0; itypeArguments; i++) { - drop(stack[frame->initialSp + i]); + for (unsigned int i = 0; itypeArguments; i++) { + drop(stack[subroutine->initialSp + i]); } //stack could look like that: @@ -254,31 +338,49 @@ namespace ts::vm2 { //in this case we have to move P1 and P2 at the beginning // | [P1] [P2] // T, T, and V were already dropped above. - if (frame->variables) { + if (subroutine->variables) { for (unsigned int i = 0; iinitialSp + i] = stack[frame->initialSp + frame->variables + i]; + stack[subroutine->initialSp + i] = stack[subroutine->initialSp + subroutine->variables + i]; } } //we want to reuse the same frame, so reset it - frame->variables = 0; - assert(frame->loop == nullptr); - sp = frame->initialSp + arguments; + subroutine->variables = 0; + assert(subroutine->loop == nullptr); + sp = subroutine->initialSp + arguments; //jump to the new address - activeSubroutine->ip = routine->address; - activeSubroutine->module = activeSubroutine->module; - activeSubroutine->subroutine = routine; - activeSubroutine->depth = activeSubroutine->depth + 1; - activeSubroutine->typeArguments = 0; + subroutine->ip = routine->address; + subroutine->module = subroutine->module; + subroutine->subroutine = routine; + subroutine->depth = subroutine->depth + 1; + subroutine->typeArguments = 0; - //debug("[{}] TailCall", activeSubroutine->ip - 4 - 2); + //debug("[{}] TailCall", subroutine->ip - 4 - 2); //printStack(); return true; } + inline ActiveSubroutine *pushSubroutine(ModuleSubroutine *routine, unsigned int arguments) { + auto nextSubroutine = activeSubroutines.push(); //&activeSubroutines[++activeSubroutineIdx]; + //important to reset necessary stuff, since we reuse + nextSubroutine->ip = routine->address; + nextSubroutine->module = subroutine->module; + nextSubroutine->subroutine = routine; + nextSubroutine->depth = subroutine->depth + 1; + nextSubroutine->typeArguments = 0; + nextSubroutine->variables = 0; + subroutine = nextSubroutine; + + //we move x arguments from the old stack frame to the new one + subroutine->initialSp = sp - arguments; + for (unsigned int i = 0; iinitialSp + i]); + } + } + inline bool call(unsigned int address, unsigned int arguments) { - auto routine = activeSubroutine->module->getSubroutine(address); + auto routine = subroutine->module->getSubroutine(address); if (routine->narrowed) { push(routine->narrowed); return false; @@ -288,31 +390,8 @@ namespace ts::vm2 { return false; } - activeSubroutine->ip++; - auto nextActiveSubroutine = activeSubroutines.push(); //&activeSubroutines[++activeSubroutineIdx]; - //important to reset necessary stuff, since we reuse - nextActiveSubroutine->ip = routine->address; - nextActiveSubroutine->module = activeSubroutine->module; - nextActiveSubroutine->subroutine = routine; - nextActiveSubroutine->depth = activeSubroutine->depth + 1; - nextActiveSubroutine->typeArguments = 0; - activeSubroutine = nextActiveSubroutine; - - auto nextFrame = frames.push(); //&frames[++frameIdx]; - nextFrame->depth = 0; - //important to reset necessary stuff, since we reuse - - // | (initialSp) [P1] [P1] (sp) | - - //debug("Call &{}[{}] {}", address, arguments, routine->name); - //printStack(); - nextFrame->initialSp = sp - arguments; //we move x arguments from the old stack frame to the new one - for (unsigned int i = 0; iinitialSp + i]->refCount, stringify(stack[nextFrame->initialSp + i]), (void *) stack[nextFrame->initialSp + i]); - use(stack[nextFrame->initialSp + i]); - } - nextFrame->variables = 0; - frame = nextFrame; + subroutine->ip++; + pushSubroutine(routine, arguments); return true; } @@ -528,8 +607,8 @@ namespace ts::vm2 { } void handleTemplateLiteral() { - auto size = activeSubroutine->parseUint16(); - auto types = frame->pop(size); + auto size = subroutine->parseUint16(); + auto types = subroutine->pop(size); //short path for `{'asd'}` if (types.size() == 1 && types[0]->kind == TypeKind::Literal) { @@ -603,7 +682,7 @@ namespace ts::vm2 { } // inline void mapFrameToChildren(Type *container) { -// auto i = frame->initialSp + frame->variables; +// auto i = subroutine->initialSp + subroutine->variables; // auto current = (TypeRef *) (container->type = useAsRef(stack[i++])); // for (; inext = useAsRef(stack[i]); @@ -613,7 +692,7 @@ namespace ts::vm2 { // //// unsigned int start = 0; //// std::span sub{stack.data() + start, sp - start}; -//// sp = frame->initialSp; +//// sp = subroutine->initialSp; //// frame = frames.pop(); //&frames[--frameIdx]; //// //// TypeRef * current = allocateRef(); @@ -629,59 +708,59 @@ namespace ts::vm2 { debug("~~~~~~~~~~~~~~~"); debug("Stack sp={}", sp); debug("~~~~~~~~~~~~~~~"); - for (int i = activeSubroutines.i; i>=0; i--) { - if (!activeSubroutines.at(i)->subroutine) { - debug("Main"); - } else { - debug("Routine {} (typeArguments={})", activeSubroutines.at(i)->subroutine->name, activeSubroutines.at(i)->typeArguments); - } - } + //for (int i = activeSubroutines.i; i>=0; i--) { + // if (!activeSubroutines.at(i)->subroutine) { + // debug("Main"); + // } else { + // debug("Routine {} (typeArguments={})", activeSubroutines.at(i)->subroutine->name, activeSubroutines.at(i)->typeArguments); + // } + //} debug("-"); - auto top = sp; - for (int i = frames.i; i>=0; i--) { - auto frame = frames.at(i); - debug("Frame {} depth={} variables={} initialSp={}", i, frame->depth, frame->variables, frame->initialSp); - - if (top>0) { - auto size = top - frame->initialSp; - for (unsigned int j = 0; jrefCount, (void *) stack[i]); - } - top -= size; - } - -// if (size != 0) { -// do { -// auto i = top - size; -// debug("{}: {} refCount={} ref={}", i, stringify(stack[i]), stack[i]->refCount, (void *) stack[i]); -// size--; -// } while (size>0); +// auto top = sp; +// for (int i = frames.i; i>=0; i--) { +// auto frame = frames.at(i); +// debug("Frame {} depth={} variables={} initialSp={}", i, subroutine->depth, subroutine->variables, subroutine->initialSp); +// +// if (top>0) { +// auto size = top - subroutine->initialSp; +// for (unsigned int j = 0; jrefCount, (void *) stack[i]); +// } +// top -= size; // } - -// do { -// if (top == 0) break; -// if (top == frame->initialSp) break; -// top--; -// } while (top != 0); - } +// +//// if (size != 0) { +//// do { +//// auto i = top - size; +//// debug("{}: {} refCount={} ref={}", i, stringify(stack[i]), stack[i]->refCount, (void *) stack[i]); +//// size--; +//// } while (size>0); +//// } +// +//// do { +//// if (top == 0) break; +//// if (top == subroutine->initialSp) break; +//// top--; +//// } while (top != 0); +// } debug("~~~~~~~~~~~~~~~"); debug(""); } inline void print(Type *type, char *title = "") { - debug("[{}] {} refCount={} {} ref={}", activeSubroutine->ip, title, type->refCount, stringify(type), (void *) type); + debug("[{}] {} refCount={} {} ref={}", subroutine->ip, title, type->refCount, stringify(type), (void *) type); } Type *handleFunction(TypeKind kind) { - const auto size = activeSubroutine->parseUint16(); + const auto size = subroutine->parseUint16(); auto type = allocate(kind); //first is the name type->type = useAsRef(pop()); - auto types = frame->pop(size); + auto types = subroutine->pop(size); auto current = (TypeRef *) type->type; //second is always the return type @@ -701,18 +780,36 @@ namespace ts::vm2 { void process() { start: - auto &bin = activeSubroutine->module->bin; + auto &bin = subroutine->module->bin; while (true) { - debug("[{}:{}] OP {} {}", activeSubroutine->depth, frame->depth, activeSubroutine->ip, (OP) bin[activeSubroutine->ip]); - switch ((OP) bin[activeSubroutine->ip]) { + //debug("[{}:{}] OP {} {}", subroutine->depth, subroutine->depth, subroutine->ip, (OP) bin[subroutine->ip]); + switch ((OP) bin[subroutine->ip]) { case OP::Halt: { -// activeSubroutine = activeSubroutines.reset(); +// subroutine = activeSubroutines.reset(); // frame = frames.reset(); // gcStack(); // gcFlush(); // printStack(); return; } + case OP::Error: { + auto ip = subroutine->ip; + const auto code = (instructions::ErrorCode) subroutine->parseUint16(); + switch (code) { + case instructions::ErrorCode::CannotFind: { + report(DiagnosticMessage(fmt::format("Cannot find name '{}'", subroutine->module->findIdentifier(ip)), ip)); + break; + } + default: { + report(DiagnosticMessage(fmt::format("{}", code), ip)); + } + } + break; + } + case OP::Pop: { + gc(pop()); + break; + } case OP::Never: { stack[sp++] = allocate(TypeKind::Never); break; @@ -734,7 +831,7 @@ namespace ts::vm2 { break; } case OP::Parameter: { - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); auto type = allocate(TypeKind::Parameter); type->readStorage(bin, address); type->type = pop(); @@ -746,14 +843,14 @@ namespace ts::vm2 { break; } case OP::FunctionRef: { - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); auto type = allocate(TypeKind::FunctionRef); type->size = address; stack[sp++] = type; break; } case OP::Instantiate: { - const auto arguments = activeSubroutine->parseUint16(); + const auto arguments = subroutine->parseUint16(); auto ref = pop(); //FunctionRef/Class switch (ref->kind) { @@ -777,8 +874,8 @@ namespace ts::vm2 { break; } case OP::CallExpression: { - const auto parameterAmount = activeSubroutine->parseUint16(); - auto parameters = frame->pop(parameterAmount); + const auto parameterAmount = subroutine->parseUint16(); + auto parameters = subroutine->pop(parameterAmount); auto typeToCall = pop(); switch (typeToCall->kind) { @@ -811,12 +908,15 @@ namespace ts::vm2 { //report(stack.errorMessage()); report(fmt::format("Argument of type '{}' is not assignable to parameter '{}' of type '{}'", stringify(lvalue), parameter->text, stringify(parameter))); } + gc(parameter); } //we could convert parameters to a tuple and then run isExtendable() on two tuples, //necessary for REST parameters. push(returnType); + + gc(typeToCall); break; } default: { @@ -826,18 +926,15 @@ namespace ts::vm2 { break; } case OP::Widen: { - auto type = pop(); - auto widened = widen(type); - push(widened); - if (widened != type) gc(type); + stack[sp - 1] = widen(stack[sp - 1]); break; } case OP::Set: { - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); auto type = pop(); - auto subroutine = activeSubroutine->module->getSubroutine(address); - if (subroutine->narrowed) drop(subroutine->narrowed); - subroutine->narrowed = use(type); + auto subroutineToSet = subroutine->module->getSubroutine(address); + if (subroutineToSet->narrowed) drop(subroutineToSet->narrowed); + subroutineToSet->narrowed = use(type); break; } case OP::Assign: { @@ -847,7 +944,7 @@ namespace ts::vm2 { if (!extends(lvalue, rvalue)) { // auto error = stack.errorMessage(); // error.ip = ip; - report(fmt::format("{} = {} not assignable", stringify(rvalue), stringify(lvalue))); + report(fmt::format("Type '{}' is not assignable to type '{}'", stringify(lvalue), stringify(rvalue))); } // ExtendableStack stack; // if (!isExtendable(lvalue, rvalue, stack)) { @@ -860,44 +957,47 @@ namespace ts::vm2 { break; } case OP::Return: { - if (activeSubroutine->isMain()) { - activeSubroutine = activeSubroutines.reset(); + if (subroutine->isMain()) { + subroutine = activeSubroutines.reset(); return; } //printStack(); //gc all parameters - for (unsigned int i = 0; itypeArguments; i++) { - if (stack[frame->initialSp + i] != stack[sp - 1]) { + for (unsigned int i = 0; itypeArguments; i++) { + if (stack[subroutine->initialSp + i] != stack[sp - 1]) { //only if the parameter is not at the same time the return value - drop(stack[frame->initialSp + i]); + drop(stack[subroutine->initialSp + i]); } else { //we decrease refCount for return value though, to remove ownership. The callee is responsible to clean it up now - stack[frame->initialSp + i]->refCount--; + stack[subroutine->initialSp + i]->refCount--; } } //the current frame could not only have the return value, but variables and other stuff, //which we don't want. So if size is bigger than 1, we move last stack entry to first // | [T] [T] [R] | - if (frame->size()>1) { - stack[frame->initialSp] = stack[sp - 1]; + if (subroutine->size()>1) { + stack[subroutine->initialSp] = stack[sp - 1]; } - sp = frame->initialSp + 1; - frame = frames.pop(); //&frames[--frameIdx]; - if (activeSubroutine->typeArguments == 0) { -// debug("keep type result {}", activeSubroutine->subroutine->name); - activeSubroutine->subroutine->result = use(stack[sp - 1]); - activeSubroutine->subroutine->result->flag |= TypeFlag::Stored; + sp = subroutine->initialSp + 1; + if (subroutine->typeArguments == 0 || subroutine->flags & SubroutineFlag::InferBody) { +// debug("keep type result {}", subroutine->subroutine->name); + subroutine->subroutine->result = use(stack[sp - 1]); + subroutine->subroutine->result->flag |= TypeFlag::Stored; } - activeSubroutine = activeSubroutines.pop(); //&activeSubroutines[--activeSubroutineIdx]; + subroutine = activeSubroutines.pop(); //&activeSubroutines[--activeSubroutineIdx]; goto start; + } + case OP::Inline: { + const auto address = subroutine->parseUint32(); + auto routine = subroutine->module->getSubroutine(address); break; } case OP::TailCall: { - const auto address = activeSubroutine->parseUint32(); - const auto arguments = activeSubroutine->parseUint16(); - //if (activeSubroutine->flag & ActiveSubroutineFlag::BlockTailCall) { + const auto address = subroutine->parseUint32(); + const auto arguments = subroutine->parseUint16(); + //if (subroutine->flag & ActiveSubroutineFlag::BlockTailCall) { // if (call(address, arguments)) { // goto start; // } @@ -909,13 +1009,54 @@ namespace ts::vm2 { break; //} } - case OP::SelfCheck: { - const auto address = activeSubroutine->parseUint32(); - //todo: this needs more definition: A type alias like `type a = T`; needs to type check as well without throwing `Generic type 'a' requires 1 type argument(s).` - auto routine = activeSubroutine->module->getSubroutine(address); + case OP::UnwrapInferBody: { + auto returnType = stack[sp - 1]; + if (returnType->size == 0) { + returnType->kind = TypeKind::Never; + } else if (returnType->size == 1) { + auto type = ((TypeRef *) returnType->type)->type; + //We do not gc(returnType) since it was loaded from TypeArgument which will be drop() later in ::Return + stack[sp - 1] = widen(type); + } else { + //We do not gc(returnType) since it was loaded from TypeArgument which will be drop() later in ::Return + stack[sp - 1] = widen(returnType); + } + break; + } + case OP::ReturnStatement: { + if (subroutine->flags & SubroutineFlag::InferBody) { + //first entry in the new stack frame is for getting all ReturnStatement calls in a union. + auto returnType = stack[subroutine->initialSp]; + auto type = pop(); + returnType->appendChild(useAsRef(type)); + } else { + + } + break; + } + case OP::InferBody: { + const auto address = subroutine->parseUint32(); + auto routine = subroutine->module->getSubroutine(address); if (routine->result) { + push(routine->result); break; } + subroutine->ip++; + pushSubroutine(routine, 0); + //subroutine is now set to a new one. + //If this is set, OP::ReturnStatement acts different + subroutine->flags |= SubroutineFlag::InferBody; + + //first entry in the new stack frame is for getting all ReturnStatement calls in a union. + //use() since it is in the place of TypeArgument, we are the owner. Will be dropped in ::Return. + push(use(allocate(TypeKind::Union))); + goto start; + } + case OP::SelfCheck: { + const auto address = subroutine->parseUint32(); + //todo: this needs more definition: A type alias like `type a = T`; needs to type check as well without throwing `Generic type 'a' requires 1 type argument(s).` + auto routine = subroutine->module->getSubroutine(address); + if (routine->result) break; if (call(address, 0)) { goto start; @@ -923,15 +1064,15 @@ namespace ts::vm2 { break; } case OP::Call: { - const auto address = activeSubroutine->parseUint32(); - const auto arguments = activeSubroutine->parseUint16(); + const auto address = subroutine->parseUint32(); + const auto arguments = subroutine->parseUint16(); if (call(address, arguments)) { goto start; } break; } //case OP::FrameReturnJump: { - // if (frame->size()>frame->variables) { + // if (subroutine->size()>subroutine->variables) { // //there is a return value on the stack, which we need to preserve // auto ret = pop(); // popFrame(); @@ -940,25 +1081,25 @@ namespace ts::vm2 { // //throw away the whole stack // popFrame(); // } - // const auto address = activeSubroutine->parseInt32(); - // //debug("FrameEndJump to {} ({})", activeSubroutine->ip + address - 4, address); - // activeSubroutine->ip += address - 4; //decrease by uint32 too + // const auto address = subroutine->parseInt32(); + // //debug("FrameEndJump to {} ({})", subroutine->ip + address - 4, address); + // subroutine->ip += address - 4; //decrease by uint32 too // goto start; //} case OP::Jump: { - const auto address = activeSubroutine->parseInt32(); - //debug("Jump to {} ({})", activeSubroutine->ip + address - 4, address); - activeSubroutine->ip += address - 4; + const auto address = subroutine->parseInt32(); + //debug("Jump to {} ({})", subroutine->ip + address - 4, address); + subroutine->ip += address - 4; goto start; } case OP::JumpCondition: { auto condition = pop(); - const auto rightProgram = activeSubroutine->parseUint32(); + const auto rightProgram = subroutine->parseUint32(); auto valid = isConditionTruthy(condition); //debug("JumpCondition {}", valid); gc(condition); if (!valid) { - activeSubroutine->ip += rightProgram - 4; + subroutine->ip += rightProgram - 4; goto start; } break; @@ -981,29 +1122,29 @@ namespace ts::vm2 { break; } case OP::Distribute: { - auto slot = activeSubroutine->parseUint16(); + auto slot = subroutine->parseUint16(); //if there is OP::Distribute, then there was always before this OP //a OP::Loads to push the type on the stack. - if (!frame->loop || frame->loop->ip != activeSubroutine->ip) { + if (!subroutine->loop || subroutine->loop->ip != subroutine->ip) { //no loop for this distribute created yet auto type = pop(); if (type->kind == TypeKind::Union) { - frame->createLoop(frame->initialSp + slot, (TypeRef *) type->type); + subroutine->createLoop(subroutine->initialSp + slot, (TypeRef *) type->type); } else { - frame->createEmptyLoop(); - stack[frame->initialSp + slot] = type; + subroutine->createEmptyLoop(); + stack[subroutine->initialSp + slot] = type; //jump over parameters, right to the distribute section - activeSubroutine->ip += 1 + 4; + subroutine->ip += 1 + 4; goto start; } } - auto next = frame->loop->next(); + auto next = subroutine->loop->next(); if (!next) { //done //printStack(); - auto types = frame->pop(sp - frame->loop->startSP); - frame->popLoop(); + auto types = subroutine->pop(sp - subroutine->loop->startSP); + subroutine->popLoop(); if (types.empty()) { push(allocate(TypeKind::Never)); } else if (types.size() == 1) { @@ -1018,36 +1159,36 @@ namespace ts::vm2 { current->next = nullptr; push(result); } - const auto loopEnd = vm::readUint32(bin, activeSubroutine->ip + 1); - activeSubroutine->ip += loopEnd - 1 - 2; + const auto loopEnd = vm::readUint32(bin, subroutine->ip + 1); + subroutine->ip += loopEnd - 1 - 2; } else { //jump over parameters, right to the distribute section - activeSubroutine->ip += 1 + 4; + subroutine->ip += 1 + 4; goto start; } break; } case OP::Loads: { - const auto varIndex = activeSubroutine->parseUint16(); - push(stack[frame->initialSp + varIndex]); + const auto varIndex = subroutine->parseUint16(); + push(stack[subroutine->initialSp + varIndex]); //debug("Loads {}:{} -> {}", frameOffset, varIndex, stringify(stack[index])); //if (frameOffset == 0) { - // push(stack[frame->initialSp + varIndex]); + // push(stack[subroutine->initialSp + varIndex]); //} else { // push(stack[frames.at(frames.i - frameOffset)->initialSp + varIndex]); //} break; } case OP::Slots: { - auto size = activeSubroutine->parseUint16(); - frame->variables += size; + auto size = subroutine->parseUint16(); + subroutine->variables += size; sp += size; break; } case OP::TypeArgumentConstraint: { auto constraint = pop(); - if (frame->size() == activeSubroutine->typeArguments) { - auto argument = stack[frame->initialSp + activeSubroutine->typeArguments]; + if (subroutine->size() == subroutine->typeArguments) { + auto argument = stack[subroutine->initialSp + subroutine->typeArguments]; if (!extends(argument, constraint)) { report(fmt::format("Type '{}' does not satisfy the constraint '{}'", stringify(argument), stringify(constraint))); } @@ -1056,47 +1197,47 @@ namespace ts::vm2 { break; } case OP::TypeArgument: { - if (frame->size()<=activeSubroutine->typeArguments) { + if (subroutine->size()<=subroutine->typeArguments) { //all variables will be dropped at the end of the subroutine push(use(allocate(TypeKind::Unknown))); } else { //for provided argument we do not increase refCount, because it's the caller's job //check constraints } - activeSubroutine->typeArguments++; - frame->variables++; + subroutine->typeArguments++; + subroutine->variables++; break; } case OP::TypeArgumentDefault: { - if (frame->size()<=activeSubroutine->typeArguments) { - activeSubroutine->typeArguments++; - frame->variables++; + if (subroutine->size()<=subroutine->typeArguments) { + subroutine->typeArguments++; + subroutine->variables++; //load default value - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); if (call(address, 0)) { //the result is pushed on the stack goto start; } } else { //for provided argument we do not increase refCount, because it's the caller's job - activeSubroutine->ip += 4; //jump over address + subroutine->ip += 4; //jump over address - activeSubroutine->typeArguments++; - frame->variables++; + subroutine->typeArguments++; + subroutine->variables++; } break; -// auto t = stack[frame->initialSp + activeSubroutine->typeArguments - 1]; +// auto t = stack[subroutine->initialSp + subroutine->typeArguments - 1]; // //t is always set because TypeArgument ensures that // if (t->flag & TypeFlag::UnprovidedArgument) { // //gc(stack[sp - 1]); // sp--; //remove unknown type from stack -// const auto address = activeSubroutine->parseUint32(); +// const auto address = subroutine->parseUint32(); // if (call(address, 0)) { //the result is pushed on the stack // goto start; // } // } else { -// activeSubroutine->ip += 4; //jump over address +// subroutine->ip += 4; //jump over address // } // break; } @@ -1152,7 +1293,7 @@ namespace ts::vm2 { } case OP::NumberLiteral: { auto item = allocate(TypeKind::Literal); - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); item->readStorage(bin, address); item->flag |= TypeFlag::NumberLiteral; stack[sp++] = item; @@ -1160,7 +1301,7 @@ namespace ts::vm2 { } case OP::StringLiteral: { auto item = allocate(TypeKind::Literal); - const auto address = activeSubroutine->parseUint32(); + const auto address = subroutine->parseUint32(); item->readStorage(bin, address); item->flag |= TypeFlag::StringLiteral; stack[sp++] = item; @@ -1213,7 +1354,7 @@ namespace ts::vm2 { break; } case OP::Class: { - const auto size = activeSubroutine->parseUint16(); + const auto size = subroutine->parseUint16(); auto item = allocate(TypeKind::Class); if (!size) { item->type = nullptr; @@ -1221,7 +1362,7 @@ namespace ts::vm2 { break; } - auto types = frame->pop(size); + auto types = subroutine->pop(size); item->type = useAsRef(types[0]); if (types.size()>1) { auto current = (TypeRef *) item->type; @@ -1235,7 +1376,7 @@ namespace ts::vm2 { break; } case OP::ObjectLiteral: { - const auto size = activeSubroutine->parseUint16(); + const auto size = subroutine->parseUint16(); auto item = allocate(TypeKind::ObjectLiteral); if (!size) { item->type = nullptr; @@ -1243,7 +1384,7 @@ namespace ts::vm2 { break; } - auto types = frame->pop(size); + auto types = subroutine->pop(size); item->type = useAsRef(types[0]); if (types.size()>1) { auto current = (TypeRef *) item->type; @@ -1257,7 +1398,7 @@ namespace ts::vm2 { break; } case OP::Union: { - const auto size = activeSubroutine->parseUint16(); + const auto size = subroutine->parseUint16(); auto item = allocate(TypeKind::Union); //printStack(); if (!size) { @@ -1265,7 +1406,7 @@ namespace ts::vm2 { push(item); break; } - auto types = frame->pop(size); + auto types = subroutine->pop(size); auto first = types[0]; if (first->kind == TypeKind::Union) { @@ -1335,7 +1476,7 @@ namespace ts::vm2 { break; } case OP::Tuple: { - const auto size = activeSubroutine->parseUint16(); + const auto size = subroutine->parseUint16(); if (size == 0) { auto item = allocate(TypeKind::Tuple); item->type = nullptr; @@ -1343,7 +1484,7 @@ namespace ts::vm2 { break; } - auto types = frame->pop(size); + auto types = subroutine->pop(size); Type *item; auto firstTupleMember = types[0]; auto firstType = (Type *) firstTupleMember->type; @@ -1452,31 +1593,31 @@ namespace ts::vm2 { break; } default: { - debug("[{}] OP {} not handled!", activeSubroutine->ip, (OP) bin[activeSubroutine->ip]); + debug("[{}] OP {} not handled!", subroutine->ip, (OP) bin[subroutine->ip]); } } - activeSubroutine->ip++; + subroutine->ip++; } } - LoopHelper *Frame::createLoop(unsigned int var1, TypeRef *type) { + LoopHelper *ActiveSubroutine::createLoop(unsigned int var1, TypeRef *type) { auto newLoop = loops.push(); newLoop->set(var1, type); - newLoop->ip = activeSubroutine->ip; + newLoop->ip = ip; newLoop->startSP = sp; newLoop->previous = loop; return loop = newLoop; } - LoopHelper *Frame::createEmptyLoop() { + LoopHelper *ActiveSubroutine::createEmptyLoop() { auto newLoop = loops.push(); - newLoop->ip = activeSubroutine->ip; + newLoop->ip = ip; newLoop->startSP = sp; newLoop->previous = loop; return loop = newLoop; } - void Frame::popLoop() { + void ActiveSubroutine::popLoop() { loop = loop->previous; loops.pop(); } diff --git a/src/checker/vm2.h b/src/checker/vm2.h index 0389ee0..b53f031 100644 --- a/src/checker/vm2.h +++ b/src/checker/vm2.h @@ -38,52 +38,6 @@ namespace ts::vm2 { void gcRefFlush(); void printStack(); - enum ActiveSubroutineFlag { - }; - - /** - * For each active subroutine this object is created. - */ - struct ActiveSubroutine { - Module *module; - ModuleSubroutine *subroutine; -// ActiveSubroutine *previous = nullptr; - - unsigned int ip = 0; //current instruction pointer -// unsigned int index = 0; - unsigned int depth = 0; - - unsigned int flag = 0; - -// bool active = true; - - unsigned int typeArguments = 0; - -// explicit ProgressingSubroutine(shared module, ModuleSubroutine *subroutine): module(module), subroutine(subroutine) {} - - uint32_t parseUint32() { - auto val = vm::readUint32(module->bin, ip + 1); - ip += 4; - return val; - } - - bool isMain() { - return !subroutine; - } - - int32_t parseInt32() { - auto val = vm::readInt32(module->bin, ip + 1); - ip += 4; - return val; - } - - uint16_t parseUint16() { - auto val = vm::readUint16(module->bin, ip + 1); - ip += 2; - return val; - } - }; - constexpr auto maxGcSize = 4069; inline std::array gcQueue; inline unsigned int gcQueueIdx; @@ -115,21 +69,27 @@ namespace ts::vm2 { } }; - enum FrameFlag: uint8_t { - //InSingleDistribute = 1<<0 + enum SubroutineFlag: uint16_t { + InferBody = 1 << 0, }; - struct Frame { + /** + * For each active subroutine this object is created. + */ + struct ActiveSubroutine { + Module *module; + ModuleSubroutine *subroutine; + unsigned int ip = 0; //current instruction pointer + unsigned int depth = 0; + unsigned int initialSp = 0; //initial stack pointer //the amount of registered variable slots on the stack. will be subtracted when doing popFrame() //type arguments of type functions and variables like for mapped types unsigned int variables = 0; + uint16_t typeArguments = 0; - //for every ActiveSubroutine this starts from 0 and increases within the subroutine. - //important to know which frame should be removed when TailCall/Return - unsigned int depth; - - uint8_t flags = 0; + /** @see SubroutineFlag */ + uint16_t flags = 0; LoopHelper *loop = nullptr; LoopHelper *createLoop(unsigned int var1, TypeRef *type); @@ -145,6 +105,27 @@ namespace ts::vm2 { sp -= size; return {stack.data() + sp, size}; } + uint32_t parseUint32() { + auto val = vm::readUint32(module->bin, ip + 1); + ip += 4; + return val; + } + + bool isMain() { + return !subroutine; + } + + int32_t parseInt32() { + auto val = vm::readInt32(module->bin, ip + 1); + ip += 4; + return val; + } + + uint16_t parseUint16() { + auto val = vm::readUint16(module->bin, ip + 1); + ip += 2; + return val; + } }; template @@ -177,19 +158,10 @@ namespace ts::vm2 { }; constexpr auto stackSize = 4069; - inline StackPool frames; inline StackPool activeSubroutines; inline StackPool loops; -// inline std::array frames; -// inline unsigned int frameIdx; - -// inline std::array activeSubroutines; -// inline unsigned int activeSubroutineIdx; - - inline Frame *frame = nullptr; - inline ActiveSubroutine *activeSubroutine = nullptr; -// inline TypeMemoryPool2 pool; + inline ActiveSubroutine *subroutine = nullptr; void process(); @@ -217,8 +189,7 @@ namespace ts::vm2 { gcQueueIdx = 0; gcQueueRefIdx = 0; sp = 0; - activeSubroutine = activeSubroutines.reset(); - frame = frames.reset(); + subroutine = activeSubroutines.reset(); loops.reset(); prepare(module); diff --git a/src/factory.h b/src/factory.h index d7f34cd..510378e 100644 --- a/src/factory.h +++ b/src/factory.h @@ -1904,19 +1904,19 @@ namespace ts { // : node; // } // -// // @api -// function createIfStatement(shared expression, thenStatement: Statement, elseStatement?: Statement) { -// auto node = createBaseNode(SyntaxKind::IfStatement); -// node->expression = expression; -// node->thenStatement = asEmbeddedStatement(thenStatement); -// node->elseStatement = asEmbeddedStatement(elseStatement); -// node->transformFlags |= -// propagateChildFlags(node->expression) | -// propagateChildFlags(node->thenStatement) | -// propagateChildFlags(node->elseStatement); -// return node; -// } -// + // @api + shared createIfStatement(shared expression, shared thenStatement, sharedOpt elseStatement = nullptr) { + auto node = createBaseNode(SyntaxKind::IfStatement); + node->expression = expression; + node->thenStatement = asEmbeddedStatement(thenStatement); + node->elseStatement = asEmbeddedStatement(elseStatement); + node->transformFlags |= + propagateChildFlags(node->expression) | + propagateChildFlags(node->thenStatement) | + propagateChildFlags(node->elseStatement); + return node; + } + // // @api // function updateIfStatement(node: IfStatement, shared expression, thenStatement: Statement, elseStatement: Statement | undefined) { // return node->expression != expression diff --git a/src/parser2.h b/src/parser2.h index 050c64e..ae3e74e 100644 --- a/src/parser2.h +++ b/src/parser2.h @@ -6842,19 +6842,19 @@ namespace ts { return withJSDoc(finishNode(factory.createEmptyStatement(), pos), hasJSDoc); } -// function parseIfStatement(): IfStatement { -// auto pos = getNodePos(); -// auto hasJSDoc = hasPrecedingJSDocComment(); -// parseExpected(SyntaxKind::IfKeyword); -// auto openParenPosition = scanner.getTokenPos(); -// auto openParenParsed = parseExpected(SyntaxKind::OpenParenToken); -// auto expression = allowInAnd>(parseExpression); -// parseExpectedMatchingBrackets(SyntaxKind::OpenParenToken, SyntaxKind::CloseParenToken, openParenParsed, openParenPosition); -// auto thenStatement = parseStatement(); -// auto elseStatement = parseOptional(SyntaxKind::ElseKeyword) ? parseStatement() : undefined; -// return withJSDoc(finishNode(factory.createIfStatement(expression, thenStatement, elseStatement), pos), hasJSDoc); -// } -// + shared parseIfStatement() { + auto pos = getNodePos(); + auto hasJSDoc = hasPrecedingJSDocComment(); + parseExpected(SyntaxKind::IfKeyword); + auto openParenPosition = scanner.getTokenPos(); + auto openParenParsed = parseExpected(SyntaxKind::OpenParenToken); + auto expression = allowInAnd>(CALLBACK(parseExpression)); + parseExpectedMatchingBrackets(SyntaxKind::OpenParenToken, SyntaxKind::CloseParenToken, openParenParsed, openParenPosition); + auto thenStatement = parseStatement(); + auto elseStatement = parseOptional(SyntaxKind::ElseKeyword) ? parseStatement() : nullptr; + return withJSDoc(finishNode(factory.createIfStatement(expression, thenStatement, elseStatement), pos), hasJSDoc); + } + // function parseDoStatement(): DoStatement { // auto pos = getNodePos(); // auto hasJSDoc = hasPrecedingJSDocComment(); @@ -7325,8 +7325,8 @@ namespace ts { return parseFunctionDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ {}, /*modifiers*/ {}); case SyntaxKind::ClassKeyword: return parseClassDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ {}, /*modifiers*/ {}); -// case SyntaxKind::IfKeyword: -// return parseIfStatement(); + case SyntaxKind::IfKeyword: + return parseIfStatement(); // case SyntaxKind::DoKeyword: // return parseDoStatement(); // case SyntaxKind::WhileKeyword: diff --git a/src/tests/test_vm2.cpp b/src/tests/test_vm2.cpp index ba01e16..ea44375 100644 --- a/src/tests/test_vm2.cpp +++ b/src/tests/test_vm2.cpp @@ -43,6 +43,13 @@ const v2: number = 123; testBench(code, 0); } +TEST_CASE("vm2BaseError1") { + string code = R"( +const v2: string = 123; + )"; + test(code, 1); +} + TEST_CASE("vm2TwoTests") { test(R"( const v1: string = "abc"; @@ -167,7 +174,6 @@ TEST_CASE("gcUnion") { TEST_CASE("gcTuple") { ts::checker::Program program; - program.pushOp(OP::Frame); for (auto i = 0; i<10; i++) { program.pushOp(OP::String); program.pushOp(OP::TupleMember); @@ -185,7 +191,6 @@ TEST_CASE("gcTuple") { TEST_CASE("gcObject") { ts::checker::Program program; - program.pushOp(OP::Frame); for (auto i = 0; i<10; i++) { program.pushOp(OP::StringLiteral); program.pushStorage("a"); @@ -556,6 +561,42 @@ TEST_CASE("function2") { testBench(code, 1); } +TEST_CASE("function3") { + string code = R"( + function doIt() { + return 1; + } + const var1: number = doIt(); + const var2: string = doIt(); +)"; + test(code, 1); + //testBench(code, 1); +} + +TEST_CASE("function4") { + string code = R"( + function doIt(v: number) { + if (v == 2) return 'yes'; + return 1; + } + const var1: number | string = doIt(0); + const var2: number = doIt(0); +)"; + test(code, 1); + testBench(code, 1); +} + +TEST_CASE("function5") { + string code = R"( + function doIt(): string { + return 1; + } + doIt(); +)"; + test(code, 1); + testBench(code, 1); +} + TEST_CASE("controlFlow1") { string code = R"( function boolFunc(t: true) {} @@ -586,6 +627,20 @@ TEST_CASE("class1") { //testBench(code, 0); } +TEST_CASE("class2") { + string code = R"( + class Date { + now(): number { + return 0; + } + } + const now: number = new Date.now(); + const now2: string = new Date.now(); +)"; + test(code, 1); + //testBench(code, 0); +} + TEST_CASE("vm2Cartesian") { { vm2::CartesianProduct cartesian;