Skip to content

Commit

Permalink
closes #292 %exec-class now checks for classes with unimplemented abs…
Browse files Browse the repository at this point in the history
…tract variants, added tests + relnotes
  • Loading branch information
davidnich committed Dec 19, 2015
1 parent ccedf75 commit 9d2946c
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 21 deletions.
1 change: 1 addition & 0 deletions doxygen/lang/900_release_notes.dox.tmpl
Expand Up @@ -374,6 +374,7 @@
- fixed bugs in relative date arithmetic where operands were swapped with the @ref minus_operator "- operator" if the first operand was a @ref relative_dates "relative date/time value", additionally an operation with the @ref minus_operator "- operator" where the first operand is a @ref relative_dates "relative date" and the second operand is a @ref absolute_dates "absolute date" is now calculated using the @ref absolute_dates "absolute date"'s epoch offset (offset in seconds and microseconds from \c 1970-01-01Z), and a @ref relative_dates "relative date/time value" is produced
- fixed a bug normalizing the result of date arithmetic between hour and minute components of @ref relative_dates "relative date/time value"
- fixed a bug where time components of absolute date/time values before the UNIX epoch were returned with invalid values
- fixed a bug where the @ref exec-class "%exec-class" directive did not check for classes with unimplemented abstract variants

@section qore_0811 Qore 0.8.11

Expand Down
33 changes: 20 additions & 13 deletions examples/test/qore/misc/parse_directives.qtest
Expand Up @@ -13,19 +13,20 @@
public class ParseDirectiveTest inherits QUnit::Test {

constructor() : Test("ParseDirectiveTest", "1.0") {
addTestCase("missing arg to %append-include-path", \missingArgAppendIncludePath(), NOTHING);
addTestCase("missing arg to %append-module-path", \missingArgAppendModulePath(), NOTHING);
addTestCase("missing arg to %set-time-zone", \missingArgSetTimeZone(), NOTHING);
addTestCase("missing arg to %enable-warning", \missingArgEnableWarning(), NOTHING);
addTestCase("missing arg to %try-module", \missingArgTryModule(), NOTHING);
addTestCase("missing arg to %requires", \missingArgRequires(), NOTHING);
addTestCase("missing arg to %requires (with space)", \missingArgRequiresWithSpace(), NOTHING);
addTestCase("tab in arg", \tabInArg(), NOTHING);
addTestCase("space after %exec-class", \spaceAfterExecClass(), NOTHING);
addTestCase("invalid id in %try-module", \invalidIdInTryModule(), NOTHING);
addTestCase("ignored text before '(' in %try-module", \ignoredTextTryModule(), NOTHING);
addTestCase("correct %try-module", \correctTryModule(), NOTHING);
addTestCase("correct %requires", \correctRequires(), NOTHING);
addTestCase("missing arg to %append-include-path", \missingArgAppendIncludePath());
addTestCase("missing arg to %append-module-path", \missingArgAppendModulePath());
addTestCase("missing arg to %set-time-zone", \missingArgSetTimeZone());
addTestCase("missing arg to %enable-warning", \missingArgEnableWarning());
addTestCase("missing arg to %try-module", \missingArgTryModule());
addTestCase("missing arg to %requires", \missingArgRequires());
addTestCase("missing arg to %requires (with space)", \missingArgRequiresWithSpace());
addTestCase("tab in arg", \tabInArg());
addTestCase("space after %exec-class", \spaceAfterExecClass());
addTestCase("invalid id in %try-module", \invalidIdInTryModule());
addTestCase("ignored text before '(' in %try-module", \ignoredTextTryModule());
addTestCase("correct %try-module", \correctTryModule());
addTestCase("correct %requires", \correctRequires());
addTestCase("%exec-class tests", \execClass());

# Return for compatibility with test harness that checks return value.
set_return_value(main());
Expand Down Expand Up @@ -137,4 +138,10 @@ $y = 9;
p.replaceParseOptions(PO_DEFAULT);
p.parse("%requires \t xml \t \nprint();\n", "");
}

execClass() {
Program p(PO_NEW_STYLE);
p.parse("%exec-class T\nclass T {abstract t();}", "exec-class");
assertThrows("ABSTRACT-CLASS-ERROR", \p.run());
}
}
15 changes: 14 additions & 1 deletion include/qore/intern/QoreClassIntern.h
Expand Up @@ -104,7 +104,7 @@ struct AbstractMethod {
assert(!vlist.empty());
}

DLLLOCAL static void parseCheckAbstract(const char* cname, const char* mname, vmap_t& vlist, QoreStringNode*& desc);
DLLLOCAL static void checkAbstract(const char* cname, const char* mname, vmap_t& vlist, QoreStringNode*& desc);

DLLLOCAL void add(MethodVariantBase* v);
DLLLOCAL void override(MethodVariantBase* v);
Expand Down Expand Up @@ -172,8 +172,13 @@ struct AbstractMethodMap : amap_t {

DLLLOCAL void parseInit(qore_class_private& qc, BCList* scl);

DLLLOCAL QoreStringNode* checkAbstract(const char* cname) const;

// we check if there are any abstract method variants still in the committed lists
DLLLOCAL void parseCheckAbstractNew(const char* name) const;

// we check if there are any abstract method variants in the class at runtime (for use with exec-class)
DLLLOCAL int runtimeCheckRunClass(const char* name, ExceptionSink* xsink) const;
};

class SignatureHash;
Expand Down Expand Up @@ -1757,6 +1762,10 @@ class qore_class_private {
return !ahm.empty();
}

DLLLOCAL int runtimeCheckRunClass(ExceptionSink* xsink) {
return ahm.runtimeCheckRunClass(name.c_str(), xsink);
}

DLLLOCAL void parseCheckAbstractNew() {
parseInit();
ahm.parseCheckAbstractNew(name.c_str());
Expand Down Expand Up @@ -2765,6 +2774,10 @@ class qore_class_private {
return qc.priv->isPublicOrPrivateMember(mem, priv);
}

DLLLOCAL static int runtimeCheckRunClass(QoreClass& qc, ExceptionSink* xsink) {
return qc.priv->runtimeCheckRunClass(xsink);
}

DLLLOCAL static void parseCheckAbstractNew(QoreClass& qc) {
qc.priv->parseCheckAbstractNew();
}
Expand Down
27 changes: 20 additions & 7 deletions lib/QoreClass.cpp
Expand Up @@ -308,8 +308,8 @@ void AbstractMethod::override(MethodVariantBase* v) {
}
}

void AbstractMethod::parseCheckAbstract(const char* cname, const char* mname, vmap_t& vlist, QoreStringNode*& desc) {
//printd(5, "AbstractMethod::parseCheckAbstract() checking %s::%s() vlist: %d\n", cname, mname, !vlist.empty());
void AbstractMethod::checkAbstract(const char* cname, const char* mname, vmap_t& vlist, QoreStringNode*& desc) {
//printd(5, "AbstractMethod::checkAbstract() checking %s::%s() vlist: %d\n", cname, mname, !vlist.empty());
if (!vlist.empty()) {
if (!desc)
desc = new QoreStringNodeMaker("class '%s' cannot be instantiated because it has the following unimplemented abstract variants:", cname);
Expand Down Expand Up @@ -419,19 +419,32 @@ void AbstractMethodMap::overrideAbstractVariant(const char* name, MethodVariantB
}
}

// we check if there are any abstract method variants still in the committed lists
void AbstractMethodMap::parseCheckAbstractNew(const char* name) const {
DLLLOCAL QoreStringNode* AbstractMethodMap::checkAbstract(const char* name) const {
if (empty())
return;
return 0;

QoreStringNode* desc = 0;
for (amap_t::const_iterator i = begin(), e = end(); i != e; ++i) {
AbstractMethod::parseCheckAbstract(name, i->first.c_str(), i->second->vlist, desc);
AbstractMethod::parseCheckAbstract(name, i->first.c_str(), i->second->pending_vlist, desc);
AbstractMethod::checkAbstract(name, i->first.c_str(), i->second->vlist, desc);
AbstractMethod::checkAbstract(name, i->first.c_str(), i->second->pending_vlist, desc);
}

//printd(5, "AbstractMethodMap::parseCheckAbstractNew() class: %s desc: %p (%s)\n", name, desc, desc ? desc->getBuffer() : "n/a");
return desc;
}

int AbstractMethodMap::runtimeCheckRunClass(const char* name, ExceptionSink* xsink) const {
QoreStringNode* desc = checkAbstract(name);
if (desc) {
xsink->raiseException("ABSTRACT-CLASS-ERROR", desc);
return -1;
}
return 0;
}

// we check if there are any abstract method variants still in the committed lists
void AbstractMethodMap::parseCheckAbstractNew(const char* name) const {
QoreStringNode* desc = checkAbstract(name);
if (desc)
parseException("ABSTRACT-CLASS-ERROR", desc);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/QoreProgram.cpp
Expand Up @@ -988,6 +988,10 @@ void QoreProgram::runClass(const char* classname, ExceptionSink* xsink) {
xsink->raiseException("CLASS-NOT-FOUND", "cannot find any class '%s' in any namespace", classname);
return;
}

if (qore_class_private::runtimeCheckRunClass(*const_cast<QoreClass*>(qc), xsink))
return;

//printd(5, "QoreProgram::runClass(%s)\n", classname);

ProgramThreadCountContextHelper tch(xsink, this, true);
Expand Down

0 comments on commit 9d2946c

Please sign in to comment.