AccessibilityController::AccessibilityController() : m_logAccessibilityEvents(false) { bindMethod("logAccessibilityEvents", &AccessibilityController::logAccessibilityEventsCallback); bindMethod("addNotificationListener", &AccessibilityController::addNotificationListenerCallback); bindMethod("removeNotificationListener", &AccessibilityController::removeNotificationListenerCallback); bindProperty("focusedElement", &AccessibilityController::focusedElementGetterCallback); bindProperty("rootElement", &AccessibilityController::rootElementGetterCallback); bindMethod("accessibleElementById", &AccessibilityController::accessibleElementByIdGetterCallback); bindFallbackMethod(&AccessibilityController::fallbackCallback); }
TextInputController::TextInputController() { bindMethod("doCommand", &TextInputController::doCommand); bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange); bindMethod("hasMarkedText", &TextInputController::hasMarkedText); bindMethod("insertText", &TextInputController::insertText); bindMethod("markedRange", &TextInputController::markedRange); bindMethod("selectedRange", &TextInputController::selectedRange); bindMethod("setMarkedText", &TextInputController::setMarkedText); bindMethod("unmarkText", &TextInputController::unmarkText); bindMethod("setComposition", &TextInputController::setComposition); }
void ColumnFixture::bind(Parse *heads) { columnBindings.clear(); for (int i = 0; heads; ++i, heads = heads->more) { QString name(heads->text()); QString suffix("()"); try { if (name.isEmpty()) columnBindings.insert(i, 0); else if (name.endsWith(suffix)) columnBindings.insert(i, bindMethod(name.mid(0, name.length() - suffix.length()))); else columnBindings.insert(i, bindField(name)); } catch (const std::exception e) { exception(heads, e); } } }
FileSharing::FileSharing(const string& url, Options& o) : uploadUrl(url), options(o), uploadLoop(bindMethod(&FileSharing::uploadingLoop, this)) { }
Object::Object() { impl = new Implementation; bindMethod(&Object::toStringMethod, "toString"); }
AccessibilityUIElement::AccessibilityUIElement(const WebAccessibilityObject& object, Factory* factory) : m_accessibilityObject(object) , m_factory(factory) { ASSERT(factory); // // Properties // bindProperty("role", &AccessibilityUIElement::roleGetterCallback); bindProperty("title", &AccessibilityUIElement::titleGetterCallback); bindProperty("description", &AccessibilityUIElement::descriptionGetterCallback); bindProperty("helpText", &AccessibilityUIElement::helpTextGetterCallback); bindProperty("stringValue", &AccessibilityUIElement::stringValueGetterCallback); bindProperty("x", &AccessibilityUIElement::xGetterCallback); bindProperty("y", &AccessibilityUIElement::yGetterCallback); bindProperty("width", &AccessibilityUIElement::widthGetterCallback); bindProperty("height", &AccessibilityUIElement::heightGetterCallback); bindProperty("intValue", &AccessibilityUIElement::intValueGetterCallback); bindProperty("minValue", &AccessibilityUIElement::minValueGetterCallback); bindProperty("maxValue", &AccessibilityUIElement::maxValueGetterCallback); bindProperty("valueDescription", &AccessibilityUIElement::valueDescriptionGetterCallback); bindProperty("childrenCount", &AccessibilityUIElement::childrenCountGetterCallback); bindProperty("insertionPointLineNumber", &AccessibilityUIElement::insertionPointLineNumberGetterCallback); bindProperty("selectedTextRange", &AccessibilityUIElement::selectedTextRangeGetterCallback); bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback); bindProperty("isRequired", &AccessibilityUIElement::isRequiredGetterCallback); bindProperty("isFocused", &AccessibilityUIElement::isFocusedGetterCallback); bindProperty("isFocusable", &AccessibilityUIElement::isFocusableGetterCallback); bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback); bindProperty("isSelectable", &AccessibilityUIElement::isSelectableGetterCallback); bindProperty("isMultiSelectable", &AccessibilityUIElement::isMultiSelectableGetterCallback); bindProperty("isSelectedOptionActive", &AccessibilityUIElement::isSelectedOptionActiveGetterCallback); bindProperty("isExpanded", &AccessibilityUIElement::isExpandedGetterCallback); bindProperty("isChecked", &AccessibilityUIElement::isCheckedGetterCallback); bindProperty("isVisible", &AccessibilityUIElement::isVisibleGetterCallback); bindProperty("isOffScreen", &AccessibilityUIElement::isOffScreenGetterCallback); bindProperty("isCollapsed", &AccessibilityUIElement::isCollapsedGetterCallback); bindProperty("hasPopup", &AccessibilityUIElement::hasPopupGetterCallback); bindProperty("isValid", &AccessibilityUIElement::isValidGetterCallback); bindProperty("isReadOnly", &AccessibilityUIElement::isReadOnlyGetterCallback); bindProperty("orientation", &AccessibilityUIElement::orientationGetterCallback); bindProperty("clickPointX", &AccessibilityUIElement::clickPointXGetterCallback); bindProperty("clickPointY", &AccessibilityUIElement::clickPointYGetterCallback); // // Methods // bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback); bindMethod("attributesOfLinkedUIElements", &AccessibilityUIElement::attributesOfLinkedUIElementsCallback); bindMethod("attributesOfDocumentLinks", &AccessibilityUIElement::attributesOfDocumentLinksCallback); bindMethod("attributesOfChildren", &AccessibilityUIElement::attributesOfChildrenCallback); bindMethod("lineForIndex", &AccessibilityUIElement::lineForIndexCallback); bindMethod("boundsForRange", &AccessibilityUIElement::boundsForRangeCallback); bindMethod("stringForRange", &AccessibilityUIElement::stringForRangeCallback); bindMethod("childAtIndex", &AccessibilityUIElement::childAtIndexCallback); bindMethod("elementAtPoint", &AccessibilityUIElement::elementAtPointCallback); bindMethod("attributesOfColumnHeaders", &AccessibilityUIElement::attributesOfColumnHeadersCallback); bindMethod("attributesOfRowHeaders", &AccessibilityUIElement::attributesOfRowHeadersCallback); bindMethod("attributesOfColumns", &AccessibilityUIElement::attributesOfColumnsCallback); bindMethod("attributesOfRows", &AccessibilityUIElement::attributesOfRowsCallback); bindMethod("attributesOfVisibleCells", &AccessibilityUIElement::attributesOfVisibleCellsCallback); bindMethod("attributesOfHeader", &AccessibilityUIElement::attributesOfHeaderCallback); bindMethod("indexInTable", &AccessibilityUIElement::indexInTableCallback); bindMethod("rowIndexRange", &AccessibilityUIElement::rowIndexRangeCallback); bindMethod("columnIndexRange", &AccessibilityUIElement::columnIndexRangeCallback); bindMethod("cellForColumnAndRow", &AccessibilityUIElement::cellForColumnAndRowCallback); bindMethod("titleUIElement", &AccessibilityUIElement::titleUIElementCallback); bindMethod("setSelectedTextRange", &AccessibilityUIElement::setSelectedTextRangeCallback); bindMethod("attributeValue", &AccessibilityUIElement::attributeValueCallback); bindMethod("isAttributeSettable", &AccessibilityUIElement::isAttributeSettableCallback); bindMethod("isPressActionSupported", &AccessibilityUIElement::isPressActionSupportedCallback); bindMethod("isIncrementActionSupported", &AccessibilityUIElement::isIncrementActionSupportedCallback); bindMethod("isDecrementActionSupported", &AccessibilityUIElement::isDecrementActionSupportedCallback); bindMethod("parentElement", &AccessibilityUIElement::parentElementCallback); bindMethod("increment", &AccessibilityUIElement::incrementCallback); bindMethod("decrement", &AccessibilityUIElement::decrementCallback); bindMethod("showMenu", &AccessibilityUIElement::showMenuCallback); bindMethod("press", &AccessibilityUIElement::pressCallback); bindMethod("isEqual", &AccessibilityUIElement::isEqualCallback); bindMethod("addNotificationListener", &AccessibilityUIElement::addNotificationListenerCallback); bindMethod("removeNotificationListener", &AccessibilityUIElement::removeNotificationListenerCallback); bindMethod("takeFocus", &AccessibilityUIElement::takeFocusCallback); bindMethod("scrollToMakeVisible", &AccessibilityUIElement::scrollToMakeVisibleCallback); bindMethod("scrollToMakeVisibleWithSubFocus", &AccessibilityUIElement::scrollToMakeVisibleWithSubFocusCallback); bindMethod("scrollToGlobalPoint", &AccessibilityUIElement::scrollToGlobalPointCallback); bindFallbackMethod(&AccessibilityUIElement::fallbackCallback); }
TextInputController::TextInputController() { bindMethod("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange); bindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint); bindMethod("conversationIdentifier", &TextInputController::conversationIdentifier); bindMethod("doCommand", &TextInputController::doCommand); bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange); bindMethod("hasMarkedText", &TextInputController::hasMarkedText); bindMethod("insertText", &TextInputController::insertText); bindMethod("makeAttributedString", &TextInputController::makeAttributedString); bindMethod("markedRange", &TextInputController::markedRange); bindMethod("selectedRange", &TextInputController::selectedRange); bindMethod("setMarkedText", &TextInputController::setMarkedText); bindMethod("substringFromRange", &TextInputController::substringFromRange); bindMethod("unmarkText", &TextInputController::unmarkText); bindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText); bindMethod("setComposition", &TextInputController::setComposition); }
TestRunner::TestRunner() : m_delegate(0) , m_webView(0) , m_intentClient(adoptPtr(new EmptyWebDeliveredIntentClient)) { // Methods implemented in terms of chromium's public WebKit API. bindMethod("setTabKeyCyclesThroughElements", &TestRunner::setTabKeyCyclesThroughElements); bindMethod("execCommand", &TestRunner::execCommand); bindMethod("isCommandEnabled", &TestRunner::isCommandEnabled); bindMethod("pauseAnimationAtTimeOnElementWithId", &TestRunner::pauseAnimationAtTimeOnElementWithId); bindMethod("pauseTransitionAtTimeOnElementWithId", &TestRunner::pauseTransitionAtTimeOnElementWithId); bindMethod("elementDoesAutoCompleteForElementWithId", &TestRunner::elementDoesAutoCompleteForElementWithId); bindMethod("numberOfActiveAnimations", &TestRunner::numberOfActiveAnimations); bindMethod("callShouldCloseOnWebView", &TestRunner::callShouldCloseOnWebView); bindMethod("setDomainRelaxationForbiddenForURLScheme", &TestRunner::setDomainRelaxationForbiddenForURLScheme); bindMethod("evaluateScriptInIsolatedWorldAndReturnValue", &TestRunner::evaluateScriptInIsolatedWorldAndReturnValue); bindMethod("evaluateScriptInIsolatedWorld", &TestRunner::evaluateScriptInIsolatedWorld); bindMethod("setIsolatedWorldSecurityOrigin", &TestRunner::setIsolatedWorldSecurityOrigin); bindMethod("setIsolatedWorldContentSecurityPolicy", &TestRunner::setIsolatedWorldContentSecurityPolicy); bindMethod("addOriginAccessWhitelistEntry", &TestRunner::addOriginAccessWhitelistEntry); bindMethod("removeOriginAccessWhitelistEntry", &TestRunner::removeOriginAccessWhitelistEntry); bindMethod("hasCustomPageSizeStyle", &TestRunner::hasCustomPageSizeStyle); bindMethod("forceRedSelectionColors", &TestRunner::forceRedSelectionColors); bindMethod("addUserScript", &TestRunner::addUserScript); bindMethod("addUserStyleSheet", &TestRunner::addUserStyleSheet); bindMethod("startSpeechInput", &TestRunner::startSpeechInput); bindMethod("loseCompositorContext", &TestRunner::loseCompositorContext); bindMethod("markerTextForListItem", &TestRunner::markerTextForListItem); bindMethod("findString", &TestRunner::findString); bindMethod("setAutofilled", &TestRunner::setAutofilled); bindMethod("setValueForUser", &TestRunner::setValueForUser); bindMethod("enableFixedLayoutMode", &TestRunner::enableFixedLayoutMode); bindMethod("setFixedLayoutSize", &TestRunner::setFixedLayoutSize); bindMethod("selectionAsMarkup", &TestRunner::selectionAsMarkup); bindMethod("setTextSubpixelPositioning", &TestRunner::setTextSubpixelPositioning); bindMethod("resetPageVisibility", &TestRunner::resetPageVisibility); bindMethod("setPageVisibility", &TestRunner::setPageVisibility); bindMethod("setTextDirection", &TestRunner::setTextDirection); bindMethod("textSurroundingNode", &TestRunner::textSurroundingNode); // The following modify WebPreferences. bindMethod("setUserStyleSheetEnabled", &TestRunner::setUserStyleSheetEnabled); bindMethod("setUserStyleSheetLocation", &TestRunner::setUserStyleSheetLocation); bindMethod("setAuthorAndUserStylesEnabled", &TestRunner::setAuthorAndUserStylesEnabled); bindMethod("setPopupBlockingEnabled", &TestRunner::setPopupBlockingEnabled); bindMethod("setJavaScriptCanAccessClipboard", &TestRunner::setJavaScriptCanAccessClipboard); bindMethod("setXSSAuditorEnabled", &TestRunner::setXSSAuditorEnabled); bindMethod("setAllowUniversalAccessFromFileURLs", &TestRunner::setAllowUniversalAccessFromFileURLs); bindMethod("setAllowFileAccessFromFileURLs", &TestRunner::setAllowFileAccessFromFileURLs); bindMethod("overridePreference", &TestRunner::overridePreference); bindMethod("setPluginsEnabled", &TestRunner::setPluginsEnabled); bindMethod("setAsynchronousSpellCheckingEnabled", &TestRunner::setAsynchronousSpellCheckingEnabled); bindMethod("setMinimumTimerInterval", &TestRunner::setMinimumTimerInterval); bindMethod("setTouchDragDropEnabled", &TestRunner::setTouchDragDropEnabled); // The following modify the state of the TestRunner. bindMethod("dumpEditingCallbacks", &TestRunner::dumpEditingCallbacks); // The following methods interact with the WebTestProxy. bindMethod("sendWebIntentResponse", &TestRunner::sendWebIntentResponse); bindMethod("deliverWebIntent", &TestRunner::deliverWebIntent); // Properties. bindProperty("workerThreadCount", &TestRunner::workerThreadCount); bindProperty("globalFlag", &m_globalFlag); bindProperty("platformName", &m_platformName); // The following are stubs. bindMethod("dumpDatabaseCallbacks", &TestRunner::notImplemented); #if ENABLE(NOTIFICATIONS) bindMethod("denyWebNotificationPermission", &TestRunner::notImplemented); bindMethod("removeAllWebNotificationPermissions", &TestRunner::notImplemented); bindMethod("simulateWebNotificationClick", &TestRunner::notImplemented); #endif bindMethod("setIconDatabaseEnabled", &TestRunner::notImplemented); bindMethod("setScrollbarPolicy", &TestRunner::notImplemented); bindMethod("clearAllApplicationCaches", &TestRunner::notImplemented); bindMethod("clearApplicationCacheForOrigin", &TestRunner::notImplemented); bindMethod("clearBackForwardList", &TestRunner::notImplemented); bindMethod("keepWebHistory", &TestRunner::notImplemented); bindMethod("setApplicationCacheOriginQuota", &TestRunner::notImplemented); bindMethod("setCallCloseOnWebViews", &TestRunner::notImplemented); bindMethod("setMainFrameIsFirstResponder", &TestRunner::notImplemented); bindMethod("setPrivateBrowsingEnabled", &TestRunner::notImplemented); bindMethod("setUseDashboardCompatibilityMode", &TestRunner::notImplemented); bindMethod("deleteAllLocalStorage", &TestRunner::notImplemented); bindMethod("localStorageDiskUsageForOrigin", &TestRunner::notImplemented); bindMethod("originsWithLocalStorage", &TestRunner::notImplemented); bindMethod("deleteLocalStorageForOrigin", &TestRunner::notImplemented); bindMethod("observeStorageTrackerNotifications", &TestRunner::notImplemented); bindMethod("syncLocalStorage", &TestRunner::notImplemented); bindMethod("addDisallowedURL", &TestRunner::notImplemented); bindMethod("applicationCacheDiskUsageForOrigin", &TestRunner::notImplemented); bindMethod("abortModal", &TestRunner::notImplemented); // The fallback method is called when an unknown method is invoked. bindFallbackMethod(&TestRunner::fallbackMethod); }
// The main bytecode interpreter loop. This is where the magic happens. It is // also, as you can imagine, highly performance critical. Returns `true` if the // fiber completed without error. static bool runInterpreter(WrenVM* vm) { // Hoist these into local variables. They are accessed frequently in the loop // but assigned less frequently. Keeping them in locals and updating them when // a call frame has been pushed or popped gives a large speed boost. register ObjFiber* fiber = vm->fiber; register CallFrame* frame; register Value* stackStart; register uint8_t* ip; register ObjFn* fn; // These macros are designed to only be invoked within this function. #define PUSH(value) (*fiber->stackTop++ = value) #define POP() (*(--fiber->stackTop)) #define DROP() (fiber->stackTop--) #define PEEK() (*(fiber->stackTop - 1)) #define PEEK2() (*(fiber->stackTop - 2)) #define READ_BYTE() (*ip++) #define READ_SHORT() (ip += 2, (ip[-2] << 8) | ip[-1]) // Use this before a CallFrame is pushed to store the local variables back // into the current one. #define STORE_FRAME() frame->ip = ip // Use this after a CallFrame has been pushed or popped to refresh the local // variables. #define LOAD_FRAME() \ frame = &fiber->frames[fiber->numFrames - 1]; \ stackStart = frame->stackStart; \ ip = frame->ip; \ if (frame->fn->type == OBJ_FN) \ { \ fn = (ObjFn*)frame->fn; \ } \ else \ { \ fn = ((ObjClosure*)frame->fn)->fn; \ } // Terminates the current fiber with error string [error]. If another calling // fiber is willing to catch the error, transfers control to it, otherwise // exits the interpreter. #define RUNTIME_ERROR(error) \ do { \ STORE_FRAME(); \ fiber = runtimeError(vm, fiber, error); \ if (fiber == NULL) return false; \ LOAD_FRAME(); \ DISPATCH(); \ } \ while (false) #if WREN_COMPUTED_GOTO // Note that the order of instructions here must exacly match the Code enum // in wren_vm.h or horrendously bad things happen. static void* dispatchTable[] = { &&code_CONSTANT, &&code_NULL, &&code_FALSE, &&code_TRUE, &&code_LOAD_LOCAL_0, &&code_LOAD_LOCAL_1, &&code_LOAD_LOCAL_2, &&code_LOAD_LOCAL_3, &&code_LOAD_LOCAL_4, &&code_LOAD_LOCAL_5, &&code_LOAD_LOCAL_6, &&code_LOAD_LOCAL_7, &&code_LOAD_LOCAL_8, &&code_LOAD_LOCAL, &&code_STORE_LOCAL, &&code_LOAD_UPVALUE, &&code_STORE_UPVALUE, &&code_LOAD_GLOBAL, &&code_STORE_GLOBAL, &&code_LOAD_FIELD_THIS, &&code_STORE_FIELD_THIS, &&code_LOAD_FIELD, &&code_STORE_FIELD, &&code_POP, &&code_CALL_0, &&code_CALL_1, &&code_CALL_2, &&code_CALL_3, &&code_CALL_4, &&code_CALL_5, &&code_CALL_6, &&code_CALL_7, &&code_CALL_8, &&code_CALL_9, &&code_CALL_10, &&code_CALL_11, &&code_CALL_12, &&code_CALL_13, &&code_CALL_14, &&code_CALL_15, &&code_CALL_16, &&code_SUPER_0, &&code_SUPER_1, &&code_SUPER_2, &&code_SUPER_3, &&code_SUPER_4, &&code_SUPER_5, &&code_SUPER_6, &&code_SUPER_7, &&code_SUPER_8, &&code_SUPER_9, &&code_SUPER_10, &&code_SUPER_11, &&code_SUPER_12, &&code_SUPER_13, &&code_SUPER_14, &&code_SUPER_15, &&code_SUPER_16, &&code_JUMP, &&code_LOOP, &&code_JUMP_IF, &&code_AND, &&code_OR, &&code_IS, &&code_CLOSE_UPVALUE, &&code_RETURN, &&code_LIST, &&code_CLOSURE, &&code_CLASS, &&code_METHOD_INSTANCE, &&code_METHOD_STATIC, &&code_END }; #define INTERPRET_LOOP DISPATCH(); #define CASE_CODE(name) code_##name #if WREN_DEBUG_TRACE_INSTRUCTIONS // Prints the stack and instruction before each instruction is executed. #define DISPATCH() \ { \ wrenDebugPrintStack(fiber); \ wrenDebugPrintInstruction(vm, fn, (int)(ip - fn->bytecode)); \ instruction = *ip++; \ goto *dispatchTable[instruction]; \ } #else #define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()]; #endif #else #define INTERPRET_LOOP for (;;) switch (instruction = READ_BYTE()) #define CASE_CODE(name) case CODE_##name #define DISPATCH() break #endif LOAD_FRAME(); Code instruction; INTERPRET_LOOP { CASE_CODE(LOAD_LOCAL_0): CASE_CODE(LOAD_LOCAL_1): CASE_CODE(LOAD_LOCAL_2): CASE_CODE(LOAD_LOCAL_3): CASE_CODE(LOAD_LOCAL_4): CASE_CODE(LOAD_LOCAL_5): CASE_CODE(LOAD_LOCAL_6): CASE_CODE(LOAD_LOCAL_7): CASE_CODE(LOAD_LOCAL_8): PUSH(stackStart[instruction - CODE_LOAD_LOCAL_0]); DISPATCH(); CASE_CODE(LOAD_LOCAL): PUSH(stackStart[READ_BYTE()]); DISPATCH(); CASE_CODE(LOAD_FIELD_THIS): { int field = READ_BYTE(); Value receiver = stackStart[0]; ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); ObjInstance* instance = AS_INSTANCE(receiver); ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field."); PUSH(instance->fields[field]); DISPATCH(); } CASE_CODE(POP): DROP(); DISPATCH(); CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH(); CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH(); CASE_CODE(TRUE): PUSH(TRUE_VAL); DISPATCH(); CASE_CODE(CALL_0): CASE_CODE(CALL_1): CASE_CODE(CALL_2): CASE_CODE(CALL_3): CASE_CODE(CALL_4): CASE_CODE(CALL_5): CASE_CODE(CALL_6): CASE_CODE(CALL_7): CASE_CODE(CALL_8): CASE_CODE(CALL_9): CASE_CODE(CALL_10): CASE_CODE(CALL_11): CASE_CODE(CALL_12): CASE_CODE(CALL_13): CASE_CODE(CALL_14): CASE_CODE(CALL_15): CASE_CODE(CALL_16): { // Add one for the implicit receiver argument. int numArgs = instruction - CODE_CALL_0 + 1; int symbol = READ_SHORT(); Value receiver = *(fiber->stackTop - numArgs); ObjClass* classObj = wrenGetClassInline(vm, receiver); // If the class's method table doesn't include the symbol, bail. if (symbol >= classObj->methods.count) { RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); } Method* method = &classObj->methods.data[symbol]; switch (method->type) { case METHOD_PRIMITIVE: { Value* args = fiber->stackTop - numArgs; // After calling this, the result will be in the first arg slot. switch (method->fn.primitive(vm, fiber, args)) { case PRIM_VALUE: // The result is now in the first arg slot. Discard the other // stack slots. fiber->stackTop -= numArgs - 1; break; case PRIM_ERROR: RUNTIME_ERROR(AS_STRING(args[0])); case PRIM_CALL: STORE_FRAME(); callFunction(fiber, AS_OBJ(args[0]), numArgs); LOAD_FRAME(); break; case PRIM_RUN_FIBER: STORE_FRAME(); fiber = AS_FIBER(args[0]); LOAD_FRAME(); break; } break; } case METHOD_FOREIGN: callForeign(vm, fiber, method->fn.foreign, numArgs); break; case METHOD_BLOCK: STORE_FRAME(); callFunction(fiber, method->fn.obj, numArgs); LOAD_FRAME(); break; case METHOD_NONE: RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); break; } DISPATCH(); } CASE_CODE(STORE_LOCAL): stackStart[READ_BYTE()] = PEEK(); DISPATCH(); CASE_CODE(CONSTANT): PUSH(fn->constants[READ_SHORT()]); DISPATCH(); CASE_CODE(SUPER_0): CASE_CODE(SUPER_1): CASE_CODE(SUPER_2): CASE_CODE(SUPER_3): CASE_CODE(SUPER_4): CASE_CODE(SUPER_5): CASE_CODE(SUPER_6): CASE_CODE(SUPER_7): CASE_CODE(SUPER_8): CASE_CODE(SUPER_9): CASE_CODE(SUPER_10): CASE_CODE(SUPER_11): CASE_CODE(SUPER_12): CASE_CODE(SUPER_13): CASE_CODE(SUPER_14): CASE_CODE(SUPER_15): CASE_CODE(SUPER_16): { // TODO: Almost completely copied from CALL. Unify somehow. // Add one for the implicit receiver argument. int numArgs = instruction - CODE_SUPER_0 + 1; int symbol = READ_SHORT(); Value receiver = *(fiber->stackTop - numArgs); ObjClass* classObj = wrenGetClassInline(vm, receiver); // Ignore methods defined on the receiver's immediate class. classObj = classObj->superclass; // If the class's method table doesn't include the symbol, bail. if (symbol >= classObj->methods.count) { RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); } Method* method = &classObj->methods.data[symbol]; switch (method->type) { case METHOD_PRIMITIVE: { Value* args = fiber->stackTop - numArgs; // After calling this, the result will be in the first arg slot. switch (method->fn.primitive(vm, fiber, args)) { case PRIM_VALUE: // The result is now in the first arg slot. Discard the other // stack slots. fiber->stackTop -= numArgs - 1; break; case PRIM_ERROR: RUNTIME_ERROR(AS_STRING(args[0])); case PRIM_CALL: STORE_FRAME(); callFunction(fiber, AS_OBJ(args[0]), numArgs); LOAD_FRAME(); break; case PRIM_RUN_FIBER: STORE_FRAME(); fiber = AS_FIBER(args[0]); LOAD_FRAME(); break; } break; } case METHOD_FOREIGN: callForeign(vm, fiber, method->fn.foreign, numArgs); break; case METHOD_BLOCK: STORE_FRAME(); callFunction(fiber, method->fn.obj, numArgs); LOAD_FRAME(); break; case METHOD_NONE: RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); break; } DISPATCH(); } CASE_CODE(LOAD_UPVALUE): { Upvalue** upvalues = ((ObjClosure*)frame->fn)->upvalues; PUSH(*upvalues[READ_BYTE()]->value); DISPATCH(); } CASE_CODE(STORE_UPVALUE): { Upvalue** upvalues = ((ObjClosure*)frame->fn)->upvalues; *upvalues[READ_BYTE()]->value = PEEK(); DISPATCH(); } CASE_CODE(LOAD_GLOBAL): PUSH(vm->globals.data[READ_SHORT()]); DISPATCH(); CASE_CODE(STORE_GLOBAL): vm->globals.data[READ_SHORT()] = PEEK(); DISPATCH(); CASE_CODE(STORE_FIELD_THIS): { int field = READ_BYTE(); Value receiver = stackStart[0]; ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); ObjInstance* instance = AS_INSTANCE(receiver); ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field."); instance->fields[field] = PEEK(); DISPATCH(); } CASE_CODE(LOAD_FIELD): { int field = READ_BYTE(); Value receiver = POP(); ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); ObjInstance* instance = AS_INSTANCE(receiver); ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field."); PUSH(instance->fields[field]); DISPATCH(); } CASE_CODE(STORE_FIELD): { int field = READ_BYTE(); Value receiver = POP(); ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); ObjInstance* instance = AS_INSTANCE(receiver); ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field."); instance->fields[field] = PEEK(); DISPATCH(); } CASE_CODE(JUMP): { int offset = READ_SHORT(); ip += offset; DISPATCH(); } CASE_CODE(LOOP): { // Jump back to the top of the loop. int offset = READ_SHORT(); ip -= offset; DISPATCH(); } CASE_CODE(JUMP_IF): { int offset = READ_SHORT(); Value condition = POP(); if (IS_FALSE(condition) || IS_NULL(condition)) ip += offset; DISPATCH(); } CASE_CODE(AND): { int offset = READ_SHORT(); Value condition = PEEK(); if (IS_FALSE(condition) || IS_NULL(condition)) { // Short-circuit the right hand side. ip += offset; } else { // Discard the condition and evaluate the right hand side. DROP(); } DISPATCH(); } CASE_CODE(OR): { int offset = READ_SHORT(); Value condition = PEEK(); if (IS_FALSE(condition) || IS_NULL(condition)) { // Discard the condition and evaluate the right hand side. DROP(); } else { // Short-circuit the right hand side. ip += offset; } DISPATCH(); } CASE_CODE(IS): { Value expected = POP(); if (!IS_CLASS(expected)) { const char* message = "Right operand must be a class."; RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); } ObjClass* actual = wrenGetClass(vm, POP()); bool isInstance = false; // Walk the superclass chain looking for the class. while (actual != NULL) { if (actual == AS_CLASS(expected)) { isInstance = true; break; } actual = actual->superclass; } PUSH(BOOL_VAL(isInstance)); DISPATCH(); } CASE_CODE(CLOSE_UPVALUE): closeUpvalue(fiber); DROP(); DISPATCH(); CASE_CODE(RETURN): { Value result = POP(); fiber->numFrames--; // Close any upvalues still in scope. Value* firstValue = stackStart; while (fiber->openUpvalues != NULL && fiber->openUpvalues->value >= firstValue) { closeUpvalue(fiber); } // If the fiber is complete, end it. if (fiber->numFrames == 0) { // If this is the main fiber, we're done. if (fiber->caller == NULL) return true; // We have a calling fiber to resume. fiber = fiber->caller; // Store the result in the resuming fiber. *(fiber->stackTop - 1) = result; } else { // Store the result of the block in the first slot, which is where the // caller expects it. stackStart[0] = result; // Discard the stack slots for the call frame (leaving one slot for the // result). fiber->stackTop = frame->stackStart + 1; } LOAD_FRAME(); DISPATCH(); } CASE_CODE(LIST): { int numElements = READ_BYTE(); ObjList* list = wrenNewList(vm, numElements); // TODO: Do a straight memcopy. for (int i = 0; i < numElements; i++) { list->elements[i] = *(fiber->stackTop - numElements + i); } // Discard the elements. fiber->stackTop -= numElements; PUSH(OBJ_VAL(list)); DISPATCH(); } CASE_CODE(CLOSURE): { ObjFn* prototype = AS_FN(fn->constants[READ_SHORT()]); ASSERT(prototype->numUpvalues > 0, "Should not create closure for functions that don't need it."); // Create the closure and push it on the stack before creating upvalues // so that it doesn't get collected. ObjClosure* closure = wrenNewClosure(vm, prototype); PUSH(OBJ_VAL(closure)); // Capture upvalues. for (int i = 0; i < prototype->numUpvalues; i++) { bool isLocal = READ_BYTE(); int index = READ_BYTE(); if (isLocal) { // Make an new upvalue to close over the parent's local variable. closure->upvalues[i] = captureUpvalue(vm, fiber, frame->stackStart + index); } else { // Use the same upvalue as the current call frame. closure->upvalues[i] = ((ObjClosure*)frame->fn)->upvalues[index]; } } DISPATCH(); } CASE_CODE(CLASS): { ObjString* name = AS_STRING(PEEK2()); ObjClass* superclass; if (IS_NULL(PEEK())) { // Implicit Object superclass. superclass = vm->objectClass; } else { // TODO: Handle the superclass not being a class object! superclass = AS_CLASS(PEEK()); } int numFields = READ_BYTE(); ObjClass* classObj = wrenNewClass(vm, superclass, numFields, name); // Don't pop the superclass and name off the stack until the subclass is // done being created, to make sure it doesn't get collected. DROP(); DROP(); // Now that we know the total number of fields, make sure we don't // overflow. if (superclass->numFields + numFields > MAX_FIELDS) { char message[70 + MAX_VARIABLE_NAME]; snprintf(message, 70 + MAX_VARIABLE_NAME, "Class '%s' may not have more than %d fields, including inherited " "ones.", name->value, MAX_FIELDS); RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); } PUSH(OBJ_VAL(classObj)); DISPATCH(); } CASE_CODE(METHOD_INSTANCE): CASE_CODE(METHOD_STATIC): { int type = instruction; int symbol = READ_SHORT(); ObjClass* classObj = AS_CLASS(PEEK()); Value method = PEEK2(); bindMethod(vm, type, symbol, classObj, method); DROP(); DROP(); DISPATCH(); } CASE_CODE(END): // A CODE_END should always be preceded by a CODE_RETURN. If we get here, // the compiler generated wrong code. UNREACHABLE(); } // We should only exit this function from an explicit return from CODE_RETURN // or a runtime error. UNREACHABLE(); return false; } WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath, const char* source) { // TODO: Check for freed VM. ObjFn* fn = wrenCompile(vm, sourcePath, source); if (fn == NULL) return WREN_RESULT_COMPILE_ERROR; WREN_PIN(vm, fn); vm->fiber = wrenNewFiber(vm, (Obj*)fn); WREN_UNPIN(vm); if (runInterpreter(vm)) { return WREN_RESULT_SUCCESS; } else { return WREN_RESULT_RUNTIME_ERROR; } }