(require 'dojo-common-containers) (defun dojo-js-assign-process-dojo-function-node (scopes class level function-node prop-name &optional existing-symbol) "Inspects all variables and assignments in the given function-node, and updates the given function-symbol with the corresponding scope (in variable value). While processing the assignments, the in-symbols and out-symbols references of the variables in the scope are updated. Tries to determine the types of that variables, of the arguments, and the return value of the function. Considers possibly already known types of arguments and return value while doing so." (log-assign level (format "Called dojo-js-assign-process-dojo-function-node for function [%s]" (if prop-name prop-name (dojo-common-node-get-function-name function-node)))) ; (log-assign level (format "Before dojo-js-assign-process-dojo-function-node ......... MEMORY: %s" (dojo-common-log-get-memory-info))) (let* ((param-nodes (js2-function-node-params function-node)) (body-node (js2-function-node-body function-node)) (arguments ()) (function-return-symbol (construct-dojo-symbol dojo-current-workspace nil nil)) (function-name (if prop-name prop-name (dojo-common-node-get-function-name function-node))) (function-symbol (construct-dojo-symbol-function dojo-current-workspace nil function-name nil function-return-symbol)) (local-scope (dojo-construct-function-param-scope function-node (1+ (length scopes)))) (local-name-to-symbol (dojo-scope-name-to-symbol local-scope)) (existing-arguments (if (and existing-symbol (dojo-core-util-is-function-symbol existing-symbol)) (dojo-symbol-get-function-arguments existing-symbol) nil)) (arg-index 0)) ; (arguments (if function-symbol (dojo-symbol-get-function-arguments function-symbol) nil))) (setf (dojo-symbol-min-pos function-symbol) (js2-node-abs-pos function-node)) (setf (dojo-symbol-max-pos function-symbol) (js2-node-abs-end function-node)) (log-assign level (format "Set position of function-symbol [%s]: (%s-%s)" (dojo-symbol-id function-symbol) (dojo-symbol-min-pos function-symbol) (dojo-symbol-max-pos function-symbol))) (dolist (param-node param-nodes) (cond ((dojo-has-node-type param-node "js2-name-node") (let* ((existing-argument (nth arg-index existing-arguments)) (name (dojo-get-name-from-name-node param-node)) (arg-symbol (construct-dojo-symbol dojo-current-workspace nil name))) (log-assign level (format "... Processing param-node [%s]" name)) (if existing-argument (progn (log-assign level (format "...... Merging existing-argument %s into arg-symbol." (dojo-core-util-symbol-to-short-string existing-argument))) (dojo-js-api-merge-api-symbol class arg-symbol existing-argument nil nil) (log-assign level (format "...... Result after merge is %s" (dojo-core-util-symbol-to-short-string arg-symbol))) (log-assign level (format "...... Full symbol: %s" arg-symbol)))) (puthash name arg-symbol local-name-to-symbol) (push arg-symbol arguments))) (t (log-extract (format "[WARNING] Ignoring function argument of node type %s" (js2-node-short-name param-node))))) (incf arg-index)) ; Lisp ideocracy: Pushing to a list by design adds at front. Thus, when processing arguments in the order of ; occurrence in source code, we need to reverse things afterwards to get the correct order. (setq arguments (nreverse arguments)) (dojo-symbol-set-function-arguments function-symbol arguments) ; For the function arguments, we have to sets of symbols: (1) The argument-symbols stored in the dojo-symbol representing the function, ; and (2) the symbols in the local-scope. Synchronize their types. ; (dolist (argument-symbol arguments) ; (log-assign level (format "... inspecting argument-symbol %s" (dojo-core-util-symbol-to-short-string argument-symbol))) ; (let* ((argument-name (dojo-symbol-name argument-symbol)) ; (local-symbol (gethash argument-name local-name-to-symbol))) ; (dojo-js-assign-set-type-or-register-assignment argument-symbol local-symbol class level t))) (log-assign level (format "Derived function-symbol %s" (dojo-core-util-symbol-to-short-string function-symbol))) (dojo-js-assign-process-block-node (append (list local-scope) scopes) class (1+ level) body-node) ; The dojo-js-assign-process-return-node calls for all return statements in the function placed ; a merge of all returned symbols under the magic key =return= in the function scope. ; Here, we collect that symbol, and set the return symbol of the function accordingly. (let ((merged-returned-symbol (dojo-core-util-get-symbol-from-scope local-scope "=return="))) (log-assign level (format "merged-returned-symbol is %s" (dojo-core-util-symbol-to-short-string merged-returned-symbol))) ; TODO in-assignment-map ; (if (and merged-returned-symbol (dojo-symbol-p merged-returned-symbol) (dojo-core-util-is-ref-symbol merged-returned-symbol)) ; (let* ((ref-symbols (dojo-symbol-get-ref-symbols merged-returned-symbol)) ; (object-symbol (nth 0 ref-symbols)) ; (member-symbol (nth 1 ref-symbols)) ; (member-symbol-id (if member-symbol (dojo-symbol-id member-symbol) nil)) ; (in-assignment-map (dojo-class-in-assignment-map class)) ; (in-assignments (gethash member-symbol-id in-assignment-map)) ; (id-to-symbol (dojo-class-id-to-symbol class))) ; ; (log-assign level (format "Trying to redirect reference member: Processing [%s] assignments..." (length in-assignments))) ; (dolist (in-assignment in-assignments) ; (let* ((source-symbol-id (dojo-assignment-source-symbol-id in-assignment)) ; (source-symbol (gethash source-symbol-id id-to-symbol))) ; (log-assign level (format "... processing assignment from %s" (dojo-core-util-symbol-to-short-string source-symbol))) ; (if source-symbol ; (progn ; (log-assign level (format "Setting member of reference %s to %s" ; (dojo-core-util-symbol-to-short-string merged-returned-symbol) ; (dojo-core-util-symbol-to-short-string source-symbol))) ; (dojo-symbol-set-ref-symbols merged-returned-symbol (list object-symbol source-symbol)))))))) (log-assign level (format "merged-returned-symbol is now: %s" (dojo-core-util-symbol-to-short-string merged-returned-symbol))) (dojo-js-assign-set-type-or-register-assignment merged-returned-symbol function-return-symbol class level)) (dojo-symbol-set-function-scope function-symbol local-scope) ; Register anonymous functions under a key in the scope. See dojo-js-key. ; Avoid saving the top-level functions of a class in the scope, as its senseless ; (we can identify them without relying on a key). ; TODO: Maybe improve the condition (currently: key not null) to really exclude ; them. (let ((key-to-symbol (dojo-js-assign-get-local-key-map scopes level)) (function-key (dojo-js-key-get-complete-node-key function-node))) (if function-key (puthash function-key function-symbol key-to-symbol))) (log-assign level (format "Finished dojo-js-assign-process-function-node, will return function as follows:")) (dojo-js-assign-log-symbol-long (1+ level) function-symbol) (dojo-js-assign-log-scope (1+ level) (dojo-symbol-get-function-scope function-symbol)) function-symbol)) ; (log-assign level (format " After dojo-js-assign-process-dojo-function-node ......... MEMORY: %s" (dojo-common-log-get-memory-info))) ;(defun dojo-js-assign-process-function-node (scopes class level function-node) ; (log-assign level (format "Called dojo-js-assign-process-function-node")) ; ; (let* ((local-symbol-map (dojo-js-assign-get-local-symbol-map scopes level)) ; (body-node (js2-function-node-body function-node))) ; ; (cond ((dojo-has-node-type body-node "js2-block-node") ; (dojo-js-assign-process-block-node scopes class (1+ level) body-node)) ; (t ; (log-completion 1 (format "WARNING: Node type [%s] of function body node is not supported." (js2-node-short-name body-node))))))) ; TODO: Wipe out function-symbol parameter (defun dojo-js-assign-process-statement-node (scopes class level node &optional existing-symbol) "Processes all kinds of nodes representing a block of Js code. Is responsible for things like processing the if or else part of a js2-if-node." (cond ((dojo-has-node-type node "js2-assign-node") (dojo-js-assign-process-assign-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-call-node") (dojo-js-assign-process-call-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-cond-node") (dojo-js-assign-process-cond-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-expr-stmt-node") (dojo-js-assign-process-expr-stmt-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-for-node") (dojo-js-assign-process-for-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-for-in-node") (dojo-js-assign-process-for-in-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-function-node") (dojo-js-assign-process-dojo-function-node scopes class (1+ level) node nil existing-symbol)) ((dojo-has-node-type node "js2-elem-get-node") (dojo-js-assign-process-elem-get-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-if-node") (dojo-js-assign-process-if-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-infix-node") (dojo-js-assign-process-infix-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-keyword-node") (dojo-js-assign-process-keyword-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-name-node") (dojo-js-assign-process-name-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-new-node") (dojo-js-assign-process-new-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-number-node") 'DOJO-JSTYPE-NUMBER) ((dojo-has-node-type node "js2-object-node") (dojo-js-assign-process-object-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-paren-node") (dojo-js-assign-process-paren-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-prop-get-node") (dojo-js-assign-process-prop-get-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-throw-node") nil) ((dojo-has-node-type node "js2-try-node") (dojo-js-assign-process-try-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-var-decl-node") (dojo-js-assign-process-var-decl-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-return-node") (dojo-js-assign-process-return-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-scope") (dojo-js-assign-process-block-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-string-node") 'DOJO-JSTYPE-STRING) ((dojo-has-node-type node "js2-unary-node") (dojo-js-assign-process-unary-node scopes class (1+ level) node)) ((dojo-has-node-type node "js2-while-node") (dojo-js-assign-process-while-node scopes class (1+ level) node)) (t (log-assign level (format "[WARNING] Node type [%s] is not yet supported by dojo-js-assign-process-statement-node" (js2-node-short-name node)))))) (defun dojo-js-assign-process-block-node (scopes class level block-node) (log-assign level (format "Called dojo-js-assign-process-block-node")) (let* ((child-nodes (js2-block-node-kids block-node)) (ret-symbol nil)) (dolist (child-node child-nodes) (setq ret-symbol (dojo-js-assign-merge-symbols ret-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) child-node)))) ret-symbol)) (defun dojo-js-assign-process-cond-node (scopes class level cond-node) (let* ((test-node (js2-cond-node-test-expr node)) (true-node (js2-cond-node-true-expr node)) (false-node (js2-cond-node-false-expr node)) (true-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) true-node)) (false-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) false-node))) ; test-node probably rather uninteresting (cond (true-symbol true-symbol) (false-symbol false-symbol) (t nil)))) (defun dojo-js-assign-log-node (prefix node) (let* ((pos (js2-node-abs-pos node)) (end (js2-node-abs-end node)) (expr-str (buffer-substring pos end)) (first-newline (string-match-p "\n" expr-str)) (expr-str-short (if (null first-newline) expr-str (substring expr-str 0 first-newline)))) (log-assign 0 (concat prefix (format ": [%s] to [%s]: [%s]" pos end expr-str-short))))) (defun dojo-js-assign-process-expr-stmt-node (scopes class level expr-stmt-node) (let* ((expr-node (js2-expr-stmt-node-expr expr-stmt-node))) (dojo-js-assign-log-node (format "EXPRESSION (%s)" (js2-node-short-name expr-node)) expr-stmt-node) (dojo-js-assign-process-statement-node scopes class (1+ level) expr-node) (dojo-js-assign-log-node (format "(end of expression (%s))" (js2-node-short-name expr-node)) expr-stmt-node))) (defun dojo-js-assign-process-if-node (scopes class level if-node) (dojo-js-assign-log-node "IF" if-node) (let* ((condition-node (js2-if-node-condition if-node)) (then-node (js2-if-node-then-part if-node)) (else-node (js2-if-node-else-part if-node)) (then-symbol nil) (else-symbol nil)) (if condition-node (progn (log-assign level (format "... processing condition-node")) (dojo-js-assign-process-statement-node scopes class (1+ level) condition-node))) (if then-node (progn (log-assign level (format "... processing then-node")) (setq then-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) then-node)))) (if else-node (progn (log-assign level (format "... processing else-node")) (setq else-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) else-node)))) (dojo-js-assign-merge-symbols then-symbol else-symbol))) (defun dojo-js-assign-process-elem-get-node (scopes class level elem-get-node &optional use-ref-symbol) "If the flag use-ref-symbol is set, and this function finds an object member access, where the member is represented by a symbol, return a DOJO-JSTYPE-REF symbol. The sense of this feature is to transfer the resolution to the calling code of e.g. some function, where certain things can be resolved on a dynamic manner." (log-assign level (format "Called process-elem-get-node, will derive left- and right-symbol...")) (let* ((left-node (js2-elem-get-node-target elem-get-node)) (right-node (js2-elem-get-node-element elem-get-node)) (left-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) left-node)) (right-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) right-node)) (left-symbol-type (dojo-core-util-symbol-or-type-to-type left-symbol-or-type)) (right-symbol-type (dojo-core-util-symbol-or-type-to-type right-symbol-or-type))) (log-assign level (format "... retrieved left-symbol %s, right-symbol %s" (dojo-core-util-symbol-to-short-string left-symbol-or-type) (dojo-core-util-symbol-to-short-string right-symbol-or-type))) ; Arrays can only have key-type DOJO-JSTYPE-NUMBER. If the symbol at hand is marked as array so far, ; but the right-symbol-type is not DOJO-JSTYPE-NUMBER, that information is wrong. Most probably, the ; symbol is a DOJO-JSTYPE-OBJECT. (if (and (or (eq left-symbol-type 'DOJO-JSTYPE-ARRAY) (eq left-symbol-type 'DOJO-JSTYPE-ARRAY-OR-OBJECT)) (not (null right-symbol-type)) (not (dojo-core-api-is-integral-type right-symbol-type))) (progn (dojo-symbol-set-type left-symbol-or-type 'DOJO-JSTYPE-OBJECT) (setq left-symbol-type 'DOJO-JSTYPE-OBJECT) ; (dojo-symbol-set-object-key-type left-symbol-or-type right-symbol-type) (dojo-workspace-register-dirty-symbol left-symbol-or-type) (log-assign level (format "Set type of left-symbol %s to OBJECT" (dojo-core-util-symbol-to-short-string left-symbol-or-type))))) (cond ((null left-symbol-or-type) (log-assign level (format "[WARNING] dojo-scopes-get-elem-get-symbol could not determine the left-symbol; it is nil and this function stops here.")) nil) ((eq left-symbol-type 'DOJO-JSTYPE-ARRAY) (dojo-symbol-get-array-value-type left-symbol-or-type)) ((eq left-symbol-type 'DOJO-JSTYPE-OBJECT) ; The type of the right-symbol is the key-type of the object. Copy the type, or (if not yet known) register the corresponding assignment. (if (null right-symbol-or-type) (log-assign level (format "[WARNING] dojo-scopes-get-elem-get-symbol could not determine the right-symbol; will proceed without evaluating it.")) (dojo-js-assign-set-type-or-register-assignment right-symbol-or-type (dojo-symbol-get-object-key-type left-symbol-or-type) class (1+ level))) ; Flag use-ref-symbol ; TODO GETSERVICE value-type if members; introduce symbols referencing to other symbols (i.e., returned symbol references to given member of object. ; Add flag to this function, to use this feature only in return statements (for now); name (local scope) --> name (method signature) is registered ; as assignment. (log-assign level (format "About to calculate returned symbol; left-symbol [%s], right-symbol [%s], use-ref-symbol [%s]" (dojo-symbol-p left-symbol-or-type) (dojo-symbol-p right-symbol-or-type) use-ref-symbol)) (cond ((and (dojo-symbol-p left-symbol-or-type) (dojo-symbol-p right-symbol-or-type)) (cond (use-ref-symbol (let ((ref-symbol (construct-dojo-symbol-ref dojo-current-workspace (dojo-class-id class) nil 'DOJO-REFTYPE-OBJECT-MEMBER (list left-symbol-or-type right-symbol-or-type)))) (log-assign level (format "Constructed ref-symbol %s" (dojo-core-util-symbol-to-short-string ref-symbol))) ref-symbol)) (t (let ((ref-symbol (construct-dojo-symbol-ref dojo-current-workspace (dojo-class-id class) nil 'DOJO-REFTYPE-OBJECT-MEMBER (list left-symbol-or-type "*")))) (log-assign level (format "Constructed ref-symbol %s" (dojo-core-util-symbol-to-short-string ref-symbol))) ref-symbol)))) (t (dojo-symbol-get-object-value-type left-symbol-or-type)))) (t (dojo-symbol-set-type left-symbol-or-type 'DOJO-JSTYPE-ARRAY-OR-OBJECT) (dojo-workspace-register-dirty-symbol left-symbol-or-type) (if (null right-symbol-or-type) (log-assign level (format "[WARNING] dojo-scopes-get-elem-get-symbol could not determine the right-symbol; will proceed without evaluating it.")) (dojo-js-assign-set-type-or-register-assignment right-symbol-or-type (dojo-symbol-get-array-or-object-key-type left-symbol-or-type) class (1+ level))) (dojo-symbol-get-array-or-object-value-type left-symbol-or-type))))) (defun dojo-js-assign-process-for-node (scopes class level for-node) (let* ((init-node (js2-for-node-init for-node)) (condition-node (js2-for-node-condition for-node)) (update-node (js2-for-node-update for-node)) (body-node (js2-loop-node-body for-node)) (var-scope (construct-dojo-scope dojo-current-workspace level "dojo-js-assign-process-for-node")) (new-scopes (append (list var-scope) scopes)) (ret-symbol nil)) (if init-node (cond ((dojo-has-node-type init-node "js2-var-decl-node") (dojo-js-assign-process-var-decl-node new-scopes class (1+ level) init-node)) (t (log-assign level (format "[WARNING] Node type [%s] of for-init-node is not supported." (js2-node-short-name init-node)))))) ; (if condition-node ; (dojo-js-assign-process-condition-node new-scopes class (1+ level) condition-node)) ; The condition-node and the update-node are probably uninteresting for us (if body-node (setq ret-symbol (dojo-js-assign-process-statement-node new-scopes class (1+ level) body-node))) ; TODO: Set up a key like: (dojo-js-assign-register-scope-in-parent-scope scopes "" var-scope) ret-symbol)) (defun dojo-js-assign-process-for-in-node (scopes class level for-in-node) (let* ((iterator-node (js2-for-in-node-iterator for-in-node)) (object-node (js2-for-in-node-object for-in-node)) (body-node (js2-loop-node-body for-in-node)) (var-scope (construct-dojo-scope dojo-current-workspace level "dojo-js-assign-process-for-in-node")) (new-scopes (append (list var-scope) scopes)) (object-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) object-node)) (ret-symbol nil)) (if iterator-node (cond ((dojo-has-node-type iterator-node "js2-var-decl-node") (dojo-js-assign-process-var-decl-node new-scopes class (1+ level) iterator-node)) (t (log-assign level (format "[WARNING] Node type [%s] of for-in-iterator-node is not supported." (js2-node-short-name iterator-node)))))) (if object-node (let ((object-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) object-node))) (if (dojo-symbol-p object-symbol-or-type) (progn (dojo-symbol-set-type object-symbol-or-type 'DOJO-JSTYPE-OBJECT) (dojo-workspace-register-dirty-symbol object-symbol-or-type))))) (if body-node (setq ret-symbol (dojo-js-assign-process-statement-node new-scopes class (1+ level) body-node))) ; TODO: Set up a key like: (dojo-js-assign-register-scope-in-parent-scope scopes "" var-scope) ret-symbol)) (defun dojo-js-assign-process-keyword-node (scopes class level node) (let* ((type (js2-node-type node))) (cond ((or (eq type js2-TRUE) (eq type js2-FALSE)) 'DOJO-JSTYPE-BOOLEAN) ((eq type js2-NULL) nil) ((eq type js2-THIS) (let ((found-symbol (dojo-scopes-get-symbol-by-name scopes (1+ level) "this"))) (log-assign level (format "Called dojo-js-assign-process-keyword-node for type js2-THIS")) (if (null found-symbol) (log-assign level (format "[WARNING] Found no symbol with name [this] in scopes"))) found-symbol)) (t (log-assign level (format "[WARNING] Unsupported keyword-type [%s] in dojo-js-assign-process-keyword-node" type)) nil)))) (defun dojo-js-assign-process-name-node (scopes class level node) (let* ((name-string (dojo-get-name-from-name-node node)) (found-symbol (dojo-scopes-get-symbol-by-name scopes (1+ level) name-string))) (log-assign level (format "Called dojo-js-assign-process-name-node for node with name-string [%s]" name-string)) (if (null found-symbol) (log-assign level (format "[WARNING] Found no symbol with name [%s] in scopes [%s]" name-string (dojo-core-util-scopes-to-string scopes)))) found-symbol)) (defun dojo-js-assign-process-new-node (scopes class level new-node) (log-assign level (format "Called dojo-js-assign-process-new-node")) (let* ((symbol (construct-dojo-symbol dojo-current-workspace nil nil))) (dojo-js-assign-derive-new-node-type scopes class (1+ level) symbol new-node) symbol)) (defun dojo-js-assign-process-object-node (scopes class level node &optional passed-object-symbol api-symbol) "Processes the given js2-object-node. The optional parameter passed-object-symbol is for the this-pointer when processing a declare-call. Then, we need to create a dedicated this symbol, and add it to the scopes array. We have chosen to do this special task outside this function, i.e. then we override the usual behaviour of creating the this-symbol inside this function. See also dojo-process-declare-call. The optional parameter api-symbol can be used to pass the counterpart api-symbol of the symbol constructed here (or passed using passed-object-symbol). This way, the code processing the subsequent nodes can use API information already known. api-symbol and the symbols below may *not* be used as return symbols though, just merging them into the newly constructed symbols is allowed." ; Fetch the element nodes of the object, and the symbols already stored in the argument node (from a possibly previous scan). ; But take care of the case, that the argument node didn't have type object at the last scan. In that case, simply start ; with an empty hash table. (log-assign level (format "Called dojo-js-assign-process-object-node")) (let* ((elem-nodes (js2-object-node-elems node)) (object-symbol (if passed-object-symbol passed-object-symbol (construct-dojo-symbol-object dojo-current-workspace nil nil))) (api-name-to-symbol (if api-symbol (dojo-symbol-get-object-members api-symbol) nil)) (key-type nil)) (dolist (object-prop-node elem-nodes) (let* ((left-node (js2-infix-node-left object-prop-node)) (prop-name (dojo-get-name-from-object-prop-left-node left-node)) (existing-symbol (if api-name-to-symbol (gethash prop-name api-name-to-symbol) nil)) (right-node (js2-infix-node-right object-prop-node)) (right-symbol (if prop-name (dojo-js-assign-process-statement-node scopes class (1+ level) right-node existing-symbol) nil))) (log-assign level (format "... Processing prop-name [%s] with right-symbol [%s]" prop-name (if (dojo-symbol-p right-symbol) (dojo-core-util-symbol-to-short-string right-symbol) right-symbol))) (cond ((null prop-name) (log-assign level (format "[WARNING] Ignoring object-prop since it has no valid prop-name."))) (t (cond ((or (dojo-has-node-type left-node "js2-name-node") (dojo-has-node-type left-node "js2-string-node")) (setq key-type 'DOJO-JSTYPE-STRING)) ((dojo-has-node-type left-node "js2-number-node") (if (null key-type) (setq key-type 'DOJO-JSTYPE-NUMBER))) (t (log-assign level (format "Unknown case [%s] of left-node type, key-type might not be exact." (js2-node-short-name right-node))))) (if (not (dojo-symbol-p right-symbol)) (setq right-symbol (construct-dojo-symbol dojo-current-workspace object-symbol prop-name right-symbol))) (let* ((member-is-api (dojo-symbol-is-api-symbol right-symbol)) (member-class-id (dojo-symbol-class-id right-symbol)) (object-is-api (dojo-symbol-is-api-symbol object-symbol)) (object-class-id (dojo-symbol-class-id object-symbol))) (if (not (eq object-class-id (dojo-class-id class))) (error (format "[ERROR] process-object-node received foreign object-symbol %s; class is [%s:%s:%s:%s]" (dojo-core-util-symbol-to-short-string object-symbol) (dojo-class-id class) (dojo-class-project class) (dojo-class-path class) (dojo-class-is-api-class class)))) (if (not (and (eq member-is-api object-is-api) (eq member-class-id object-class-id))) (progn (setq right-symbol (dojo-js-api-clone-api-symbol class right-symbol)) (log-assign level (format "Right-symbol differs from object-symbol in is-api/class-id; cloned it with result %s" (dojo-core-util-symbol-to-short-string right-symbol)))))) ; Deciding the name of the right-symbol here is a bit non-trivial. ; On the one hand, e.g. anonymous functions defined in a object need to be named according to the prop-name. ; On the other hand, we need to avoid damaging e.g. i18n symbols, something like 'label : i18n.foo' --- ; in that case, symbol 'foo' must not be renamed to 'label' here. ; Hopefully, the simple solution to only set the name here if it wasn't yet set works for all cases; otherwise ; we might need some more sophisticated strategy. ; Alternative ways to deal with the situation might be (a) a case distinction on the right-symbol type and ; properties, or (b) simply cloning right-symbol (which of course would come with some performance penality). (if (dojo-symbol-name right-symbol) (dojo-js-assign-merge-object-value-type object-symbol right-symbol) (setf (dojo-symbol-name right-symbol) prop-name) (setf (dojo-symbol-min-pos right-symbol) (js2-node-abs-pos object-prop-node)) (setf (dojo-symbol-max-pos right-symbol) (js2-node-abs-end object-prop-node))) (dojo-symbol-register-object-member prop-name right-symbol object-symbol))))) (dojo-symbol-set-object-key-type object-symbol (construct-dojo-symbol dojo-current-workspace object-symbol nil key-type)) object-symbol)) (defun dojo-js-assign-merge-object-value-type (object-symbol member-symbol) (let* ((value-type (dojo-symbol-get-object-value-type object-symbol)) (old-type (if (dojo-symbol-p value-type) (dojo-symbol-type value-type) nil)) (member-type (if (dojo-symbol-p member-symbol) (dojo-symbol-type member-symbol) nil))) ; TODO implement )) (defun dojo-js-assign-process-paren-node (scopes class level node) (let* ((expr-node (js2-paren-node-expr node))) (log-assign level (format "Called dojo-js-assign-process-paren-node")) (dojo-js-assign-process-statement-node scopes class (1+ level) expr-node))) (defun dojo-js-assign-process-prop-get-node (scopes class level node &optional allow-transient) (log-assign level (format "Called dojo-js-assign-process-prop-get-node")) (let* ((left-node (js2-infix-node-left node)) (right-node (js2-infix-node-right node)) (left-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) left-node))) (log-assign level (format "process-prop-get-node derived left-symbol-or-type %s" (dojo-core-util-symbol-to-short-string left-symbol-or-type))) (cond ((null left-symbol-or-type) (log-assign level (format "[WARNING] Did not find a symbol for left-node, will ignore it."))) ((not (dojo-symbol-p left-symbol-or-type)) (log-assign level (format "[WARNING] Derived primitive type [%s] for left-node, will ignore it." left-symbol-or-type))) (t (let* ((left-symbol (dojo-scopes-follow-symbol left-symbol-or-type (1+ level))) (right-symbol-name (if (dojo-has-node-type right-node "js2-name-node") (dojo-get-name-from-name-node right-node) nil))) ; Certain classes in dojo-provider work using assignments ; var create = exports.create = function create(...) { ... } ; where exports is an import with path 'exports'. Treat that symbol as magic ; import, and assign anything to the static-symbol of our class. (cond ((and left-symbol (dojo-core-util-is-import-symbol left-symbol)) (if (string= (dojo-symbol-get-import-path left-symbol) "exports") (let* ((static-symbol (dojo-class-static-symbol class))) (setq left-symbol static-symbol) (log-assign level (format "Found magic 'exports' import, stepping to static-symbol [%s] of class" (dojo-core-util-symbol-to-short-string left-symbol))))))) ; Bascially, we cannot find out much about the type of the left-symbol here. ; - it might be a function if e.g. apply() is called, but objects with function apply aren't forbidden as well ; - it might be an array if e.g. push() is called, but objects with function push aren't forbidden as well ; Thus, we don't touch the type of the left-symbol here, but just work with it. (if (and right-symbol-name (not (gethash right-symbol-name dojo-js-non-object-functions)) (not (dojo-core-util-is-import-symbol left-symbol)) (not (dojo-core-util-is-ref-symbol left-symbol))) (progn (log-assign level (format "... Declaring left-symbol %s OBJECT, since right-symbol name [%s] does not indicate anything else" (dojo-core-util-symbol-to-short-string left-symbol) right-symbol-name)) (dojo-symbol-set-type left-symbol 'DOJO-JSTYPE-OBJECT nil t))) (log-assign level (format "... Found left-symbol %s" (dojo-core-util-symbol-to-short-string left-symbol))) (cond ((dojo-core-util-is-object-symbol left-symbol) (cond (right-symbol-name (let* ((right-symbol (gethash right-symbol-name (dojo-symbol-get-object-members left-symbol)))) (if (null right-symbol) (progn (setq right-symbol (construct-dojo-symbol dojo-current-workspace left-symbol right-symbol-name nil)) (dojo-symbol-register-object-member right-symbol-name right-symbol left-symbol) (log-assign level (format "... Created right-symbol %s" (dojo-core-util-symbol-to-short-string right-symbol)))) (log-assign level (format "... Found existing right-symbol %s" (dojo-core-util-symbol-to-short-string right-symbol)))) (if (string= right-symbol-name "domNode") (progn (dojo-symbol-set-type right-symbol 'DOJO-JSTYPE-DOMNODE) (log-assign level (format "... Found right-node-name 'domNode', set type accordingly, right-symbol now %s" (dojo-core-util-symbol-to-short-string right-symbol))))) (log-assign level (format "... process-prop-get-node returns right-symbol %s" (dojo-core-util-symbol-to-short-string right-symbol))) right-symbol)) (t (log-assign level (format "[WARNING] dojo-js-assign-process-prop-get-node in case OBJECT ignores unsupported right-node type [%s]" (js2-node-short-name right-node)))))) ((and allow-transient (dojo-core-util-is-ref-symbol left-symbol) (eq (dojo-symbol-get-ref-type left-symbol) 'DOJO-REFTYPE-OBJECT-MEMBER)) (cond (right-symbol-name (let* ((left-name (dojo-symbol-name left-symbol)) (left-ref-symbols (dojo-symbol-get-ref-symbols left-symbol)) (transient-ref-symbol (construct-transient-dojo-symbol left-name 'DOJO-JSTYPE-REF)) (transient-ref-symbols ())) (dojo-symbol-set-ref-type transient-ref-symbol 'DOJO-REFTYPE-OBJECT-MEMBER) (dolist (left-ref-symbol left-ref-symbols) (push left-ref-symbol transient-ref-symbols)) (push right-symbol-name transient-ref-symbols) (dojo-symbol-set-ref-symbols transient-ref-symbol (nreverse transient-ref-symbols)) (log-assign level (format "process-prop-get-symbol returns transient ref symbol: %s" (dojo-core-util-symbol-to-short-string transient-ref-symbol))) transient-ref-symbol)) (t (log-assign level (format "[WARNING] dojo-js-assign-process-prop-get-node in case REF ignores unsupported right-node type [%s]" (js2-node-short-name right-node)))))) ((null (dojo-symbol-type left-symbol)) (log-assign level (format "... no type of left-symbol known so far, will return nil.")) nil) (t (log-assign level (format "[WARNING] dojo-js-assign-process-prop-get-node found unsupported left-symbol %s" (dojo-core-util-symbol-to-short-string left-symbol)))))))))) (defun dojo-js-assign-process-return-node (scopes class level return-node) ; TODO: Maybe process case "expression to return, or 'undefined" (from js2-return-node doc) (log-assign level (format "Called process-return-node, for return-node [%s] - [%s]" (js2-node-abs-pos return-node) (js2-node-abs-end return-node))) (let* ((retval-node (js2-return-node-retval return-node)) ; If some object member access is returned, return a reference symbol. ; For doing so, bypass process-statement-node, and call process-elem-get-node ; directly with the corresponding flag set. See also process-elem-get-node. (returned-symbol (cond ((dojo-has-node-type retval-node "js2-elem-get-node") (dojo-js-assign-process-elem-get-node scopes class (1+ level) retval-node t)) (t (if retval-node (dojo-js-assign-process-statement-node scopes class (1+ level) retval-node))))) (fct-scope (nth 0 scopes)) (old-returned-symbol (if fct-scope (dojo-core-util-get-symbol-from-scope fct-scope "=return=") nil)) (merged-returned-symbol (dojo-js-assign-merge-symbols old-returned-symbol returned-symbol))) (log-assign level (format "Called dojo-js-assign-process-return-node, retrieved returned-symbol %s" (dojo-core-util-symbol-to-short-string returned-symbol))) ; We register the returned symbol (to be precise: the merge of all returned symbols, if there are multiple return statements) ; under the magic key =return= in the function scope. ; When processing a function, dojo-js-assign-process-dojo-function-node processes the body node, in particular calls this function ; for all return statements of the function, and collects the resulting =return= symbol from its scope afterwards. ; This way, we can transfer the merged return symbol to the function return symbol without relying on return values of ; various functions, etc. (dojo-core-util-register-in-scope fct-scope "=return=" merged-returned-symbol) returned-symbol)) (defun dojo-js-assign-process-infix-node (scopes class level infix-node) (let* ((type (js2-node-type infix-node)) (left-node (js2-infix-node-left infix-node)) (right-node (js2-infix-node-right infix-node)) (is-in-number-out-bool (or (eq type js2-LT) (eq type js2-LE) (eq type js2-GT) (eq type js2-GE))) (is-in-number-out-number (or (eq type js2-DIV) (eq type js2-MUL) (eq type js2-MOD) (eq type js2-SUB)))) (log-assign level (format "Called dojo-js-assign-process-infix-node for type [%s]" type)) (cond ((or (eq type js2-EQ) (eq type js2-NE)) (dojo-js-assign-process-statement-node scopes class (1+ level) left-node) (dojo-js-assign-process-statement-node scopes class (1+ level) right-node) 'DOJO-JSTYPE-BOOLEAN) ((or (eq type js2-OR) (eq type js2-AND)) (let* ((left-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) left-node)) (right-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) right-node))) ; In Javascript, || and && don't return boolean, but one of the two operand types. This is e.g. used ; by expressions like "(foo || bar).x" that can sometimes be found in Dojo library code. ; As a rough estimate, we can try to just return the first type, i.e. left-symbol-or-type here. ; However, propagating the type between the operands is certainly not appropriate here, since ; otherwise in an expression like "while (fooObject && !myString)", fooObject would wrongly receive ; type BOOLEAN, as !myString evaluates to type BOOLEAN... left-symbol-or-type)) ((eq type js2-ADD) (let* ((left-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) left-node)) (right-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) right-node)) (left-type (if (dojo-symbol-p left-symbol-or-type) (dojo-symbol-type left-symbol-or-type) left-symbol-or-type)) (right-type (if (dojo-symbol-p right-symbol-or-type) (dojo-symbol-type right-symbol-or-type) right-symbol-or-type))) (cond ((and (null left-type) (null right-type)) nil) ((or (eq left-type 'DOJO-JSTYPE-STRING) (eq right-type 'DOJO-JSTYPE-STRING)) 'DOJO-JSTYPE-STRING) (t (log-assign level (format "[WARNING] Unsupported left-type [%s] and right-type [%s] in dojo-js-assign-process-infix-node, case js2-ADD" left-type right-type)) nil)))) ((or is-in-number-out-bool is-in-number-out-number) ; [>= > <= < / * % -] (let ((left-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) left-node)) (right-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) right-node))) (if (dojo-symbol-p left-symbol-or-type) (progn (dojo-symbol-set-type left-symbol-or-type 'DOJO-JSTYPE-NUMBER) (dojo-workspace-register-dirty-symbol left-symbol-or-type) (log-assign level (format "left-symbol of operator [%s] is now [%s %s %s]" type (dojo-symbol-id left-symbol-or-type) (dojo-core-util-symbol-type-to-string left-symbol-or-type) (dojo-symbol-name left-symbol-or-type)))) (log-assign level (format "left-type is [%s]" left-symbol-or-type))) (if (dojo-symbol-p right-symbol-or-type) (progn (dojo-symbol-set-type right-symbol-or-type 'DOJO-JSTYPE-NUMBER) (dojo-workspace-register-dirty-symbol right-symbol-or-type) (log-assign level (format "right-symbol of operator [%s] is now [%s %s %s]" type (dojo-symbol-id right-symbol-or-type) (dojo-core-util-symbol-type-to-string right-symbol-or-type) (dojo-symbol-name right-symbol-or-type)))) (log-assign level (format "right-type is [%s]" right-symbol-or-type)))) (cond (is-in-number-out-bool 'DOJO-JSTYPE-BOOLEAN) (is-in-number-out-number 'DOJO-JSTYPE-NUMBER))) ((eq type js2-IN) ; The right operand of the IN operator is definitely an DOJO-JSTYPE-OBJECT (let* ((right-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) right-node))) (if right-symbol (progn (dojo-symbol-set-type right-symbol 'DOJO-JSTYPE-OBJECT) (dojo-workspace-register-dirty-symbol right-symbol))))) (t (log-assign level (format "[WARNING] infix-node type [%s] is unsupported by dojo-js-assign-process-infix-node." type)) nil)))) (defun dojo-js-assign-process-unary-node (scopes class level unary-node) (log-assign level (format "Called dojo-js-assign-process-unary-node")) (let* ((operator (js2-node-type unary-node)) (operand (js2-unary-node-operand unary-node)) (operand-symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) operand))) (cond ((eq operator js2-NOT) 'DOJO-JSTYPE-BOOLEAN) ((eq operator js2-DELPROP) nil) ((or (eq operator js2-INC) (eq operator js2-DEC)) (if (dojo-symbol-p operand-symbol-or-type) (dojo-symbol-set-type operand-symbol-or-type 'DOJO-JSTYPE-NUMBER)) 'DOJO-JSTYPE-NUMBER) (t (log-assign level (format "[WARNING] Not yet supported operator [%s] in dojo-js-assign-process-unary-node" operator)))))) (defun dojo-js-assign-process-while-node (scopes class level while-node) (let* ((condition-node (js2-while-node-condition while-node)) (body-node (js2-loop-node-body while-node)) (var-scope (construct-dojo-scope dojo-current-workspace level "dojo-js-assign-process-while-node")) (new-scopes (append (list var-scope) scopes)) (ret-symbol nil)) (log-assign level (format "Called dojo-js-assign-process-while-node")) (if condition-node (dojo-js-assign-process-statement-node new-scopes class (1+ level) condition-node)) (if body-node (setq ret-symbol (dojo-js-assign-process-statement-node new-scopes class (1+ level) body-node))) ; TODO: Set up a key like: (dojo-js-assign-register-scope-in-parent-scope scopes "" var-scope) ret-symbol)) (defun dojo-js-assign-process-assign-node (scopes class level assign-node) (let* ((left-node (js2-infix-node-left assign-node)) (right-node (js2-infix-node-right assign-node)) (operator (js2-infix-node-type assign-node)) (left-symbol (dojo-scopes-get-node-symbol scopes (1+ level) left-node t class)) (right-symbol nil)) (log-assign level (format "Called dojo-js-assign-process-assign-node, with left-node of type [%s], right-node of type [%s]" (if left-node (js2-node-short-name left-node) "---") (if right-node (js2-node-short-name right-node) "---"))) (cond ((null left-symbol) (log-assign level (format "Found no left-symbol"))) (t (log-assign level (format "dojo-js-assign-process-assign-node found left-symbol %s; operator: [%s]" (dojo-core-util-symbol-to-short-string left-symbol) (dojo-js2-node-type-to-string operator))) (cond ((eq operator js2-ASSIGN) (cond ((dojo-has-node-type right-node "js2-new-node") (dojo-js-assign-derive-new-node-type scopes class (1+ level) left-symbol right-node) (log-assign level (format "EXTRACTED js-ASSIGN for NEW, derived symbol %s" (dojo-core-util-symbol-to-short-string left-symbol)))) ((dojo-has-node-type right-node "js2-array-node") (dojo-js-assign-derive-new-array-type scopes class (1+ level) left-symbol right-node)) (t (setq right-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) right-node)) ; Nasty special case of defining Dojo classes. In Dojo library, statements like ; var AdapterRegistry = dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){ ; can be found. Essentially, this is a definition of the constructor function of class AdapterRegistry. ; First of all, we need to detect that case, i.e. the following code is only triggered if a ; function() is assigned to the dojo-object of a dojo/_base/kernel import. ; If that (rather complicate) condition is met, we want to propagate that information ('constructor') ; in an appropriate manner, i.e. we ; (a) mark the function the constructor function ; (b) lateron return the right-symbol as return value of this function. ; This way, we can have the function in the scope when processing the top level statements. ; ; See also dojo-extract-class-process-define-body, special treatment of returned function symbols with ; name constructor (if (and right-symbol (dojo-symbol-p right-symbol) (dojo-core-util-is-function-symbol right-symbol)) (let ((left-symbol-parent (dojo-symbol-get-parent dojo-current-workspace left-symbol))) (if (and left-symbol-parent (dojo-core-util-is-object-symbol left-symbol-parent) (eq (dojo-symbol-get-object-object-type left-symbol-parent) 'DOJO-OBJECTTYPE-STATIC)) (let* ((import-class-id (dojo-symbol-parent-id left-symbol-parent)) (import-class (if (not (null import-class-id)) (dojo-core-api-get-class-by-id dojo-current-workspace import-class-id) nil)) (import-class-path (if import-class (dojo-class-path import-class) nil))) (if (string= import-class-path "dojo/_base/kernel") (progn (log-assign level (format (concat "Found assignment of function %s to static-symbol of dojo/_base/kernel import; " "will make the function the constructor function.") (dojo-core-util-symbol-to-short-string right-symbol))) (setf (dojo-symbol-name right-symbol) "constructor")) ))))) (if (null right-symbol) (log-assign level (format "Found neither js2-new-node, nor right-symbol")) (dojo-js-assign-set-type-or-register-assignment right-symbol left-symbol class (1+ level)) right-symbol)))) ((eq operator js2-ASSIGN_ADD) (let* ((right-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) right-node)) (right-type (if (dojo-symbol-p right-symbol) (dojo-symbol-type right-symbol) right-symbol))) ; The right hand operator of += can be either a string, or a number. In the number case, we don't know ; wether we do a " += '5'", or a += 5. In the string case, at least a programming ; style like += "5" should be fairly seldom. (if (eq right-type 'DOJO-JSTYPE-STRING) (dojo-js-assign-set-type-or-register-assignment right-type left-symbol class (1+ level))))) ((or (eq operator js2-ASSIGN_SUB) (eq operator js2-ASSIGN_MUL) (eq operator js2-ASSIGN_DIV) (eq operator js2-ASSIGN_MOD)) (dojo-js-assign-set-type-or-register-assignment 'DOJO_JSTYPE_NUMBER left-symbol class (1+ level))) (t (log-assign level (format "WARNING: Operator [%s] is unsupported in dojo-js-assign-process-assign-node." (dojo-js2-node-type-to-string operator))))))))) (defun dojo-js-assign-get-local-symbol-map (scopes level) (let* ((local-scope (nth 0 scopes)) (local-name-to-symbol (dojo-scope-name-to-symbol local-scope))) local-name-to-symbol)) (defun dojo-js-assign-get-local-key-map (scopes level) (let* ((local-scope (nth 0 scopes)) (local-key-to-symbol (dojo-scope-key-to-symbol local-scope))) local-key-to-symbol)) (defun dojo-js-assign-process-try-node (scopes class level node) (let* ((try-block-node (js2-try-node-try-block node)) (catch-nodes (js2-try-node-catch-clauses node)) (finally-node (js2-try-node-finally-block node)) (try-symbol nil) (catch-symbol nil) (finally-symbol nil)) (if try-block-node (setq try-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) try-block-node))) (if catch-nodes (dolist (catch-node catch-nodes) (setq catch-symbol (dojo-js-assign-process-catch-node scopes class (1+ level) catch-node)))) (if finally-node (setq finally-symbol (dojo-js-assign-process-finally-node scopes class (1+ level) finally-node))) (dojo-js-assign-merge-symbols try-symbol (dojo-js-assign-merge-symbols catch-symbol finally-symbol)))) (defun dojo-js-assign-process-catch-node (scopes class level catch-node) (let* ((param-node (js2-catch-node-param catch-node)) (catch-param-scopes (dojo-js-assign-get-catch-param-scopes scopes level param-node))) (dojo-js-assign-process-block-node catch-param-scopes class (1+ level) catch-node) ; TODO Add key like (dojo-js-assign-register-scope-in-parent-scope scopes "" (nth 0 catch-param-scopes)))) (defun dojo-js-assign-get-catch-param-scopes (scopes level catch-param-node) (cond ((dojo-has-node-type catch-param-node "js2-name-node") (let* ((var-name (dojo-get-name-from-name-node catch-param-node)) (var-scope (construct-dojo-scope dojo-current-workspace level "dojo-js-assign-get-catch-param-scopes")) (var-scope-name-to-symbol (dojo-scope-name-to-symbol var-scope)) (exception-symbol (construct-dojo-symbol dojo-current-workspace nil var-name 'DOJO-JSTYPE-EXCEPTION))) (puthash var-name exception-symbol var-scope-name-to-symbol) (append (list var-scope) scopes))) (t (log-assign level (format "[WARNING] Node type [%s] is not supported as catch-param-node in dojo-js-assign-get-catch-param-scopes" (js2-node-short-name catch-param-node))) nil))) (defun dojo-js-assign-process-finally-node (scopes class level finally-node) (let* ((body-node (js2-finally-node-body finally-node))) (dojo-js-assign-process-statement-node scopes class (1+ level) body-node))) (defun dojo-js-assign-process-var-decl-node (scopes class level var-decl-node) (dolist (var-init-node (js2-var-decl-node-kids var-decl-node)) (let* ((target-node (js2-var-init-node-target var-init-node)) (init-node (js2-var-init-node-initializer var-init-node)) (target-node-name (cond ((dojo-has-node-type target-node "js2-name-node") (dojo-get-name-from-name-node target-node)) (t (log-assign level (format "[WARNING] Node type [%s] is not supported for the target-node of a var-init-node." (js2-node-short-name target-node))) nil))) (symbol (if target-node-name (construct-dojo-symbol dojo-current-workspace nil target-node-name) nil)) (name-to-symbol (dojo-js-assign-get-local-symbol-map scopes level))) (log-assign level (format "process-var-decl-node created symbol [%s] for local variable [%s]; init-node [%s]" (dojo-core-util-symbol-to-short-string symbol) target-node-name (if init-node (js2-node-short-name init-node) "---"))) (puthash target-node-name symbol name-to-symbol) (cond ; E.g. the var-decl-node of a for-in-node has no init-node ((null init-node) nil) ((dojo-has-node-type init-node "js2-new-node") (dojo-js-assign-derive-new-node-type scopes class (1+ level) symbol init-node)) ((dojo-has-node-type init-node "js2-array-node") (dojo-js-assign-derive-new-array-type scopes class (1+ level) symbol init-node)) ((dojo-has-node-type init-node "js2-number-node") (dojo-symbol-set-type symbol 'DOJO-JSTYPE-NUMBER) (dojo-workspace-register-dirty-symbol symbol)) (t (let ((symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) init-node))) (dojo-js-assign-set-type-or-register-assignment symbol-or-type symbol class (1+ level)) (log-assign level (format "After register-assignment: symbol-or-type %s, symbol %s" (dojo-core-util-symbol-to-short-string symbol-or-type) (dojo-core-util-symbol-to-short-string symbol))))))))) (defun dojo-js-assign-derive-new-node-type (scopes class level left-symbol new-node) (let* ((target-node (js2-new-node-target new-node)) ) (cond ((dojo-has-node-type target-node "js2-name-node") (dojo-js-assign-set-new-symbol-type-by-name scopes class level left-symbol new-node (dojo-get-name-from-name-node target-node))) (t (log-assign level (format "[WARNING] Ignoring not yet supported node of type [%s]" (js2-node-short-name target-node)))) ) ) ) (defun dojo-js-assign-set-new-symbol-type-by-name (scopes class level symbol new-node name) ; The constructor-symbol is the symbol the statement after the 'new' evaluates to. ; Example: In 'dojo.behavior = new Behavior();', the constructor-symbol is the local ; variable 'Behaviour' known at that point of code. ; In particular, the constructor-symbol doesn't need to be connected with any import ; (i.e. something like 'new MyWidget();', it may simply be some function defined ; somewhere. The latter style can be found inside the Dojo library, less in our own ; code. (cond ((string= name "Object") (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derived type Object")) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-OBJECT t)) ((string= name "Image") (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derived type Image")) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-IMAGE t)) ((string= name "Number") (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derived type Number")) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-NUMBER t)) ((string= name "RegExp") (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derived type RegExp")) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-REGEXP t)) ((string= name "Date") (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derived type Date")) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-DATE t)) (t (let* ((constructor-symbol (dojo-scopes-get-symbol-by-name scopes (1+ level) name))) (cond ((and constructor-symbol (dojo-core-util-is-function-symbol constructor-symbol)) (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name found constructor-symbol %s of type FUNCTION" (dojo-core-util-symbol-to-short-string constructor-symbol))) (let* ((constructor-function-scope (dojo-symbol-get-function-scope constructor-symbol)) (constructor-function-this-symbol (dojo-core-util-get-symbol-from-scope constructor-function-scope "this"))) (cond ((null constructor-function-this-symbol) (log-assign level (format "Could not find a this-symbol for constructor-symbol %s; will make the symbol an empty OBJECT symbol." (dojo-core-util-symbol-to-short-string constructor-symbol))) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-OBJECT t)) ((not (dojo-core-util-is-object-symbol constructor-function-this-symbol)) (log-assign level (format "this-symbol %s for constructor-symbol %s is not an OBJECT; will make the symbol an empty OBJECT symbol." (dojo-core-util-symbol-to-short-string constructor-function-this-symbol) (dojo-core-util-symbol-to-short-string constructor-symbol))) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-OBJECT t)) (t (dojo-core-util-copy-additional-attributes constructor-function-this-symbol symbol t) (log-assign level (format "Set symbol to %s based on constructor-function-this-symbol %s" (dojo-core-util-symbol-to-short-string symbol) (dojo-core-util-symbol-to-short-string constructor-function-this-symbol))))))) (t (log-assign level (format "dojo-js-assign-set-new-symbol-type-by-name derives type...")) (let* ((class-id (dojo-class-id class)) (id-to-api-class (dojo-workspace-id-to-api-class dojo-current-workspace)) (api-class (gethash class-id id-to-api-class))) (log-assign level (format "... class: id [%s], project [%s], path [%s]" (if class (dojo-class-id class) "---") (if class (dojo-class-project class) "---") (if class (dojo-class-path class) "---"))) (log-assign level (format "... api-class is [%s]" (if api-class (dojo-class-path api-class) "---")))) ;dojo-core-util-get-class-by-import-symbol workspace import-symbol (let* ((import-to-symbol (dojo-class-import-to-symbol class)) (import-symbol (gethash name import-to-symbol)) ; TODO: Code like "var MyGrid = declare([OnDemandGrid, DijitRegistry, Selection]);" ... new MyGrid ; leads to import-symbol being nil here; to support such statements we would need to ; properly process such declare statements. (import-resource-id (if import-symbol (dojo-symbol-get-import-resource-id import-symbol) nil)) (import-type (if import-symbol (dojo-symbol-get-import-type import-symbol) nil)) (import-path (if import-symbol (dojo-symbol-get-import-path import-symbol) nil)) (import-api-class (if import-symbol (dojo-js-api-get-class-by-import-symbol import-symbol) nil)) (constructor-fct-symbol (if import-api-class (dojo-core-api-get-or-create-member dojo-current-workspace import-api-class "constructor" 'DOJO-JSTYPE-FUNCTION) nil)) (arg-nodes (js2-new-node-args new-node))) (cond ((null import-symbol) (log-assign level (format (concat "[WARNING] When processing new-node, import-symbol for name [%s] could not be found; " "a possible cause is an unsupported call like 'var MyGrid = declare([OnDemandGrid, DijitRegistry, Selection]);'") name))) ((string= import-path "clazzes/LoginGuardRpcService") (log-assign level (format "Found constructor call of LoginGuardRpcService.")) (dojo-js-assign-derive-new-loginguard-rpc scopes class level symbol arg-nodes)) (t (log-assign level (format "Found import-symbol [%s]" import-symbol)) (log-assign level (format "Found constructor-fct-symbol [%s]" constructor-fct-symbol)) (log-assign level (format "Found import-resource-id = [%s], import-path = [%s], import-api-class = [%s]" import-resource-id import-path (if import-api-class (dojo-class-path import-api-class) "nil"))) (cond ((not (null import-symbol)) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-INSTANCE t) (dojo-symbol-set-import-resource-id symbol import-resource-id) (dojo-symbol-set-import-type symbol import-type) (dojo-symbol-set-import-path symbol import-path) (if constructor-fct-symbol (dojo-js-assign-process-api-function-arguments scopes class import-api-class level constructor-fct-symbol arg-nodes))) (t (log-assign level (format "WARNING: dojo-js-assign-set-new-symbol-type-by-name cannot resolve name [%s]" name))))))))))))) (defun dojo-js-assign-output-class-and-api-class (class prefix) (log-assign 0 (format "%s: id [%s], project [%s], path [%s], for resource with id [%s], path [%s], file-path [%s]" prefix (dojo-class-id class) (dojo-class-project class) (dojo-class-path class) (dojo-resource-id resource) (dojo-resource-path resource) (dojo-resource-file-path resource))) (let* ((id-to-api-class (dojo-workspace-id-to-api-class dojo-current-workspace)) (api-class (gethash (dojo-class-id class) id-to-api-class))) (log-assign 0 (format "... api-class has id [%s], project [%s], path [%s]" (if api-class (dojo-class-id api-class) "---") (if api-class (dojo-class-project api-class) "---") (if api-class (dojo-class-path api-class) "---"))))) (defun dojo-js-assign-derive-new-array-type (scopes class level symbol init-node) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-ARRAY t) (let* ((elem-nodes (js2-array-node-elems init-node))) (dolist (elem-node elem-nodes) (let ((value-type-symbol (construct-dojo-symbol dojo-current-workspace symbol nil))) (cond ((dojo-has-node-type elem-node "js2-new-node") (dojo-js-assign-derive-new-node-type scopes class (1+ level) value-type-symbol elem-node)) (t (log-assign level (format "[WARNING] dojo-js-assign-derive-new-array-type does not yet support elem-node type [%s]" (js2-node-short-name elem-node))))) (if (not (null (dojo-symbol-type value-type-symbol))) (progn (dojo-symbol-set-array-value-type symbol value-type-symbol) (return nil))))))) (defun dojo-js-assign-derive-new-loginguard-rpc (scopes class level symbol arg-nodes) (let* ((class-id (dojo-class-id class)) (arg-node (nth 0 arg-nodes)) (call-node (if (dojo-has-node-type arg-node "js2-call-node") arg-node nil)) (call-target-node (if call-node (js2-call-node-target call-node) nil)) (call-prop-get-node (if (dojo-has-node-type call-target-node "js2-prop-get-node") call-target-node nil)) (call-object-node (if call-prop-get-node (js2-infix-node-left call-prop-get-node) nil)) (call-name-node (if (dojo-has-node-type call-object-node "js2-name-node") call-object-node nil)) (call-object-symbol (if call-name-node (dojo-scopes-get-symbol-by-name scopes (1+ level) (dojo-get-name-from-name-node call-name-node)) nil))) (log-assign level (format "Found call-object-symbol [%s]" (dojo-core-util-symbol-to-short-string call-object-symbol))) (if (and (not (null call-object-symbol)) (dojo-core-util-is-import-symbol call-object-symbol) (string= (dojo-symbol-get-import-path call-object-symbol) "dojo/json") (not (null (js2-infix-node-right call-prop-get-node)))) (let* ((call-fct-node (if (dojo-has-node-type (js2-infix-node-right call-prop-get-node) "js2-name-node") (js2-infix-node-right call-prop-get-node) nil)) (call-fct-name (if call-fct-node (dojo-get-name-from-name-node call-fct-node) nil)) (call-args (if (and call-fct-name (string= call-fct-name "parse")) (js2-call-node-args call-node) nil)) (first-call-arg (nth 0 call-args)) (first-call-node (if (and first-call-arg (dojo-has-node-type first-call-arg "js2-name-node")) first-call-arg nil)) (call-arg-symbol (if first-call-node (dojo-scopes-get-symbol-by-name scopes (1+ level) (dojo-get-name-from-name-node first-call-node)) nil)) (call-arg-import-symbol (if (and call-arg-symbol (dojo-core-util-is-import-symbol call-arg-symbol) (eq (dojo-symbol-get-import-type call-arg-symbol) 'DOJO-IMPORT-SMD)) call-arg-symbol nil)) (smd-import-path (if call-arg-import-symbol (dojo-symbol-get-import-path call-arg-import-symbol) nil)) (project-name (dojo-class-project class))) (if smd-import-path (progn (log-assign level (format "Found smd-import-path [%s], project-name is [%s]" smd-import-path project-name)) (blueprint-parse-for-project-if-necessary project-name) (let* ((project (dojo-core-workspace-get-project-by-class class)) (blueprint-file (if project (dojo-project-blueprint-file project) nil))) (log-assign level (format "Found blueprint-file: [%s]" (if blueprint-file "Yes" "No"))) ; TODO: Although most of the HttpServiceRegistrationListener registrations live in the own ; project, the case that we would need to query some other project services.xml here exists. ; Example: jobStatusService implemented in cdes-dojo-impl, but used in cdes-web. (let* ((qualified-interface-name (if blueprint-file (blueprint-util-get-interface-by-smd blueprint-file smd-import-path) nil)) (name-to-symbol (make-hash-table :test 'equal))) (log-assign level (format "... Derived interface [%s]" qualified-interface-name)) (dojo-symbol-set-type symbol 'DOJO-JSTYPE-OBJECT t) (dojo-symbol-set-object-object-type symbol 'DOJO-OBJECTTYPE-SERVICE) (let* ((path-to-resources (dojo-workspace-path-to-resources dojo-current-workspace)) (interface-resources (if qualified-interface-name (gethash qualified-interface-name path-to-resources) nil)) (interface-resource (if (listp interface-resources) (nth 0 interface-resources) nil)) (interface-resource-id (if interface-resource (dojo-resource-id interface-resource) nil)) (resource-file-path (if interface-resource (dojo-resource-file-path interface-resource) nil))) (log-assign level (format "... Derived interface resource [%s] with file-path [%s]" interface-resource-id resource-file-path)) (dojo-symbol-set-object-object-info symbol interface-resource-id)) (dojo-symbol-set-object-members symbol name-to-symbol) )) )))))) (defun dojo-js-assign-process-call-node (scopes class level call-node) (log-assign level (format "Called process-call-node")) (let* ((target-node (js2-call-node-target call-node)) (function-symbol (cond ((dojo-has-node-type target-node "js2-prop-get-node") ; Allow returning reference symbols. (dojo-js-assign-process-prop-get-node scopes class level target-node t)) (t (dojo-js-assign-process-statement-node scopes class level target-node)))) (arg-nodes (js2-call-node-args call-node))) (cond ((null function-symbol) (log-assign level (format "[WARNING] Did not receive any function-symbol, will ignore this call-node"))) ((dojo-core-util-is-ref-symbol function-symbol) (log-assign level (format "... received ref-symbol as function, will process it...")) (let* ((ref-symbol function-symbol) (ref-type (dojo-symbol-get-ref-type ref-symbol)) (ref-symbols (dojo-symbol-get-ref-symbols ref-symbol)) (first-ref-symbol (nth 0 ref-symbols)) (second-ref-symbol (nth 1 ref-symbols)) (third-ref-symbol (nth 2 ref-symbols))) (if (and (eq ref-type 'DOJO-REFTYPE-OBJECT-MEMBER) (dojo-core-util-is-object-symbol first-ref-symbol) (string= second-ref-symbol "*") (stringp third-ref-symbol)) (progn (log-assign level (format "... case .*.foo")) (let ((name-to-object-member (dojo-symbol-get-object-members first-ref-symbol))) (maphash (lambda (name object-member) (log-assign level (format "...... processing object member [%s]" name)) (cond ((dojo-core-util-is-object-symbol object-member) (let ((object-member-member (dojo-symbol-get-object-member third-ref-symbol object-member))) (if object-member-member (progn (log-assign level (format "......... calling do-process-call-node for object-member-member")) (dojo-js-assign-do-process-call-node scopes class level call-node object-member-member)) (log-assign level (format "......... [WARNING] Did not find object-member-member [%s].[%s]" name third-ref-symbol))))) (t (log-assign level (format "......... [WARNING] object member [%s] is no object symbol." name))))) name-to-object-member))) (log-assign level (format "[WARNING] Found ref-symbol, but with no supported case; will ignore this call-node"))))) (t (dojo-js-assign-do-process-call-node scopes class level call-node function-symbol))))) (defun dojo-js-assign-do-process-call-node (scopes class level call-node function-symbol) (log-assign level (format "... do-process-call-node received function-symbol %s" (dojo-core-util-symbol-to-short-string function-symbol))) ; If we have found an object symbol, that at the same time is a function symbol, ; switch to the corresponding function-symbol ; (example: dojo/on is a function, and on.emit is one of its properties) (if (and function-symbol (dojo-core-util-is-import-symbol function-symbol)) (progn (setq function-symbol (dojo-core-util-get-import-static-symbol dojo-current-workspace function-symbol)) (log-assign level (format "Switching to static-symbol %s of import-symbol" (dojo-core-util-symbol-to-short-string function-symbol))))) (if (and function-symbol (dojo-core-util-is-object-symbol function-symbol) (not (null (dojo-symbol-get-object-function-symbol function-symbol)))) (let ((object-function-symbol (dojo-symbol-get-object-function-symbol function-symbol))) (log-assign level (format "Switching to function-symbol %s of object-symbol %s" (dojo-core-util-symbol-to-short-string object-function-symbol) (dojo-core-util-symbol-to-short-string function-symbol))) (setq function-symbol object-function-symbol))) (if (null function-symbol) ; Sometimes, while inspecting the prop-get-node being the target-node in the sense of this function, ; we fail to determine the left-symbol type of that prop-get node. For example, foo.apply might ; indicate that the left-symbol is a function - but it might as well be an object on which a function ; called apply is defined. ; Then, we end up without function-symbol here. But at least, we can inspect the arg-nodes ; in an isolated manner - after all, they might contain non-trivial content, e.g. some anonymous function. (progn (log-assign level "Did not find a function-symbol for call node, will only process arguments-nodes.") (dolist (arg-node arg-nodes) (dojo-js-assign-process-statement-node scopes class level arg-node)) nil) ; Declare the function-symbol a FUNCTION, if not yet done (if (not (dojo-core-util-is-function-symbol function-symbol)) (progn (dojo-symbol-set-type function-symbol 'DOJO-JSTYPE-FUNCTION) (log-assign level (format "Found symbol and set its type: %s" (dojo-core-util-symbol-to-short-string function-symbol))))) (let* ((builtin-fct (dojo-symbol-get-function-builtin-fct function-symbol))) (if builtin-fct (progn (log-assign level (format "Extracted builtin function symbol [%s] (fct [%s %s %s])" builtin-fct (dojo-symbol-id function-symbol) (dojo-core-util-symbol-type-to-string function-symbol) (dojo-symbol-name function-symbol))) (dojo-js-assign-derive-arg-types scopes class (1+ level) function-symbol arg-nodes) (dojo-symbol-get-function-return-type function-symbol)) ; (dojo-symbol-set-type function-symbol 'DOJO-JSTYPE-FUNCTION t) ; (log-assign level (format "Extracted symbol %s for function call" ; (dojo-symbol-id function-symbol) (dojo-core-util-symbol-type-to-string function-symbol) (dojo-symbol-name function-symbol))) (let* ((process-args-ret (dojo-js-assign-process-special-function-call scopes class (1+ level) function-symbol call-node)) (process-arguments (nth 0 process-args-ret)) (process-return-type (nth 1 process-args-ret))) (if process-arguments (dojo-js-assign-derive-arg-types scopes class (1+ level) function-symbol arg-nodes)) (if process-return-type ; In rare cases, while processing the function arguments, a dojo-function is ; converted to a dojo-object with function-symbol set. In that case, we need ; to jump to the function-symbol here. (let ((fct-symbol (dojo-core-util-step-to-function function-symbol))) (if fct-symbol (let ((return-type (dojo-symbol-get-function-return-type fct-symbol))) (cond ((and return-type (dojo-core-util-is-ref-symbol return-type)) (let* ((ref-context (dojo-js-assign-get-call-ref-context call-node)) (arg-symbols (dojo-symbol-get-function-arguments function-symbol))) (let* ((new-return-type (dojo-js-assign-follow-call-ref-symbol level return-type arg-symbols ref-context))) ; (log-dep (format "Checking for function ref return value dependency for fct %s and return-type %s" ; (dojo-core-util-symbol-to-short-string fct-symbol) ; (dojo-core-util-symbol-to-short-string new-return-type))) (if (dojo-core-dep-register-function-call-dependency fct-symbol new-return-type class) (progn (if (not (dojo-workspace-plan-extract-pending dojo-current-workspace)) (log-workspace (format (concat "[TRIGGER-PLAN-EXTRACT] Triggered planning the extraction of dependent resources, " "since processing the return type of function %s changed dependency information.") (if function-symbol (dojo-core-util-symbol-to-short-string function-symbol) "[---]")))) (dojo-core-workspace-trigger-plan-extractions))) new-return-type))) (t ; (log-dep (format "Checking for function return value dependency for fct %s and return-type %s" ; (dojo-core-util-symbol-to-short-string fct-symbol) ; (dojo-core-util-symbol-to-short-string return-type))) (if (dojo-core-dep-register-function-call-dependency fct-symbol return-type class) (progn (if (not (dojo-workspace-plan-extract-pending dojo-current-workspace)) (log-workspace (format (concat "[TRIGGER-PLAN-EXTRACT] Triggered planning the extraction of dependent resources, " "since processing the return type of function %s changed dependency information.") (if function-symbol (dojo-core-util-symbol-to-short-string function-symbol) "[---]")))) (dojo-core-workspace-trigger-plan-extractions))) return-type))))))))))) (defun dojo-js-assign-derive-arg-types (scopes class level fct-symbol arg-nodes) "Tries to derive the parameter types of a call to the function with the given fct-symbol, with given arguments." (log-assign level (format "Called dojo-js-assign-derive-arg-types for fct-symbol %s" (dojo-core-util-symbol-to-short-string fct-symbol))) (let* ((arg-symbols (dojo-symbol-get-function-arguments fct-symbol))) (dotimes (n (length arg-nodes)) (log-assign level (format "... Processing arg-node [%s]" n)) (let* ((arg-node (nth n arg-nodes)) (old-arg-symbol (nth n arg-symbols)) (new-arg-symbol nil)) (if (null old-arg-symbol) (progn (setq new-arg-symbol (construct-dojo-symbol dojo-current-workspace fct-symbol nil)) (setq arg-symbols (append arg-symbols (list new-arg-symbol)))) ; Not that efficient, but for small argument lists it should be ok (setq new-arg-symbol old-arg-symbol)) (dojo-js-assign-populate-argument-symbol-from-node scopes class (1+ level) new-arg-symbol arg-node))) (log-assign level (format "... Fct-symbol is %s" (dojo-core-util-symbol-to-short-string fct-symbol))) (setq fct-symbol (dojo-core-util-step-to-function fct-symbol)) (dolist (arg-symbol arg-symbols) ; (log-dep (format "Checking for function argument dependency for fct %s and arg %s" ; (dojo-core-util-symbol-to-short-string fct-symbol) ; (dojo-core-util-symbol-to-short-string arg-symbol))) (if (dojo-core-dep-register-function-call-dependency fct-symbol arg-symbol class) (progn (if (not (dojo-workspace-plan-extract-pending dojo-current-workspace)) (log-assign (format (concat "[TRIGGER-PLAN-EXTRACT] Triggered planning the extraction of dependent resources, " "since processing argument [%s] of function [%s] changed dependency information.") (if arg-symbol (dojo-symbol-name arg-symbol) "---") (if fct-symbol (dojo-core-util-symbol-to-short-string fct-symbol) "[---]")))) (dojo-core-workspace-trigger-plan-extractions)))) ; In rare cases, while processing the function arguments, a dojo-function is ; converted to a dojo-object with function-symbol set. In that case, we need ; to jump to the function-symbol here. (if fct-symbol (dojo-symbol-set-function-arguments fct-symbol arg-symbols) (log-assign level (format "[WARNING] Found no fct-symbol at the end of dojo-js-assign-derive-arg-types"))))) (defun dojo-js-assign-process-api-function-arguments (scopes class api-class level api-fct-symbol arg-nodes) (log-assign level (format "Called dojo-js-assign-process-api-function-arguments for fct-symbol %s" (dojo-core-util-symbol-to-short-string api-fct-symbol))) (let* ((api-fct-arguments (dojo-symbol-get-function-arguments api-fct-symbol))) (while (< (length api-fct-arguments) (length arg-nodes)) (setq api-fct-arguments (append api-fct-arguments (list (construct-dojo-symbol dojo-current-workspace api-class nil))))) (dojo-symbol-set-function-arguments api-fct-symbol api-fct-arguments) (dolist (api-fct-argument api-fct-arguments) ; (log-dep (format "Checking for constructor function argument dependency for fct %s and arg %s" ; (dojo-core-util-symbol-to-short-string api-fct-symbol) ; (dojo-core-util-symbol-to-short-string api-fct-argument))) (dojo-core-dep-register-function-call-dependency api-fct-symbol api-fct-argument class)) (dotimes (n (length arg-nodes)) (let* ((arg-node (nth n arg-nodes)) (passed-symbol (dojo-js-assign-process-statement-node scopes class level arg-node)) (api-arg-symbol (nth n api-fct-arguments))) (log-assign level (format "... will map passed-symbol %s to api-arg-symbol %s" (dojo-core-util-symbol-to-short-string passed-symbol) (dojo-core-util-symbol-to-short-string api-arg-symbol))) (if (dojo-symbol-p passed-symbol) (dojo-js-assign-register-assignment class passed-symbol api-arg-symbol) (dojo-symbol-set-type api-arg-symbol passed-symbol t t))))) (log-assign level (format "Result is %s" (dojo-core-util-symbol-to-short-string api-fct-symbol)))) (defun dojo-js-assign-populate-argument-symbol-from-node (scopes class level arg-symbol arg-node) "Populates the given argument symbol with data, based on the given argument node matching an argument in a function call like f(a, b, c). I.e., this function attempts to determine the type of some function parameter, based on a call to that function. If an literal is passed (something like f(1)), its type is used directly. If a variable etc. is passed, which is represented by a dojo-symbol, then its type is used if already known. Otherwise, a call to dojo-js-assign-register-assignment is performed." (log-assign level (format "Called dojo-js-assign-populate-argument-symbol-from-node, for node [%s] - [%s]" (js2-node-abs-pos arg-node) (js2-node-abs-end arg-node))) (cond ((dojo-has-node-type arg-node "js2-object-node") (dojo-js-assign-populate-object-argument-symbol scopes class level arg-symbol arg-node)) ((dojo-has-node-type arg-node "js2-prop-get-node") (dojo-js-assign-populate-prop-get-argument-symbol scopes class level arg-symbol arg-node)) ((dojo-has-node-type arg-node "js2-call-node") (dojo-js-assign-populate-call-argument-symbol scopes class level arg-symbol arg-node)) ; ((dojo-has-node-type arg-node "js2-name-node") ; (dojo-js-assign-populate-name-argument-symbol scopes class level arg-symbol arg-node)) (t (let* ((symbol-or-type (dojo-js-assign-process-statement-node scopes class (1+ level) arg-node))) ; If the code above could not resolve a symbol matching the argument, in some cases, we can ; do some reasoning purely based on the arg-node type and content. (if (null symbol-or-type) ; If literally 'this' is passed, we might not be able to resolve the this pointer from ; the scopes. But at least, we know that the corresponding argument has type DOJO-JSTYPE-OBJECT. ; But do only touch the arg-symbol if it doesn't yet have type DOJO-JSTYPE-OBJECT, to ; avoid loss of information (e.g. key-type, value-type). (cond ((and (dojo-has-node-type arg-node "js2-keyword-node") (eq (js2-node-type arg-node) js2-THIS) (not (dojo-core-util-is-object-symbol arg-symbol))) (log-assign (1+ level) (format "Setting type of argument to OBJECT because this (though unresolvable) was passed.")) (setf symbol-or-type 'DOJO-JSTYPE-OBJECT)) ((and (dojo-has-node-type arg-node "js2-name-node") (string= (dojo-get-name-from-name-node arg-node) "arguments") (not (dojo-core-util-is-array-symbol arg-symbol))) (log-assign (1+ level) (format "Setting type of argument to ARRAY because the arguments object is passed.")) (setf symbol-or-type 'DOJO-JSTYPE-ARRAY)))) (dojo-js-assign-set-type-or-register-assignment symbol-or-type arg-symbol class (1+ level)))))) (defun dojo-js-assign-populate-object-argument-symbol (scopes class level arg-symbol arg-node) ; Fetch the element nodes of the object, and the symbols already stored in the argument node (from a possibly previous scan). ; But take care of the case, that the argument node didn't have type object at the last scan. In that case, simply start ; with an empty hash table. (log-assign level (format "Called dojo-js-assign-populate-object-argument-symbol for symbol %s in class [%s]" (dojo-core-util-symbol-to-short-string arg-symbol) (dojo-symbol-class-id arg-symbol))) ; (log-assign level (format "... in id-to-class: %s" (if (gethash (dojo-symbol-class-id arg-symbol) (dojo-workspace-id-to-class dojo-current-workspace)) "yes" "no"))) ; (log-assign level (format "... in id-to-api-class: %s" (if (gethash (dojo-symbol-class-id arg-symbol) (dojo-workspace-id-to-api-class dojo-current-workspace)) ; "yes" "no"))) (let* ((elem-nodes (js2-object-node-elems arg-node)) (name-to-symbol (if (dojo-core-util-is-object-symbol arg-symbol) (dojo-symbol-get-object-members arg-symbol) (make-hash-table :test 'equal)))) (dojo-symbol-set-type arg-symbol 'DOJO-JSTYPE-OBJECT t) (dolist (object-prop-node elem-nodes) (let* ((left-node (js2-infix-node-left object-prop-node)) (prop-name (dojo-get-name-from-object-prop-left-node left-node)) (right-node (js2-infix-node-right object-prop-node)) (old-symbol (if prop-name (gethash prop-name name-to-symbol) nil)) (new-symbol (cond ((null prop-name) nil) (old-symbol old-symbol) (t (construct-dojo-symbol dojo-current-workspace arg-symbol prop-name))))) (if (null prop-name) (log-assign level (format "[WARNING] Could not determine a valid prop-name, will ignore this property.")) (puthash prop-name new-symbol name-to-symbol) (log-data (format "Registered [object %s %s].[member %s %s] (except case (not-is-object-symbol arg-symbol))" (dojo-symbol-id arg-symbol) (dojo-symbol-name arg-symbol) (dojo-symbol-id new-symbol) prop-name)) (dojo-js-assign-populate-argument-symbol-from-node scopes class (1+ level) new-symbol right-node)))))) ; (setf (dojo-symbol-value arg-symbol) name-to-symbol))) ;(defun dojo-js-assign-populate-keyword-argument-symbol (scopes level arg-symbol arg-node) ; (let* ((type (js2-node-type arg-node))) ; (cond ((or (eq type js2-TRUE) (eq type js2-FALSE)) ; (log-assign level (format "dojo-js-assign-populate-keyword-argument-symbol sets symbol type to [boolean]")) ; (dojo-symbol-set-type arg-symbol 'DOJO-JSTYPE-BOOLEAN)) ; (t ; (log-assign level (format "[WARNING] Unsupported keyword-type [%s] in dojo-js-assign-populate-keyword-argument-symbol" type)))))) (defun dojo-js-assign-populate-prop-get-argument-symbol (scopes class level arg-symbol arg-node) (log-assign level (format "Called dojo-js-assign-populate-prop-get-argument-symbol for symbol [%s %s %s]" (dojo-symbol-id arg-symbol) (dojo-core-util-symbol-type-to-string arg-symbol) (dojo-symbol-name arg-symbol))) (let* ((symbol (dojo-scopes-get-node-symbol scopes (1+ level) arg-node t class))) (if (null symbol) (log-assign (1+ level) "Did not find symbol for prop-get-node") (if (dojo-symbol-p symbol) (log-assign level (format "Derived symbol [%s %s %s]" (dojo-symbol-id symbol) (dojo-core-util-symbol-type-to-string symbol) (dojo-symbol-name symbol))) (log-assign level (format "Dervied symbol type [%s]" symbol))) (dojo-js-assign-set-type-or-register-assignment symbol arg-symbol class (1+ level))))) (defun dojo-js-assign-populate-call-argument-symbol (scopes class level arg-symbol arg-node) (log-assign level (format "Called dojo-js-assign-populate-call-argument-symbol for symbol [%s %s %s], from-to [%s] - [%s]" (dojo-symbol-id arg-symbol) (dojo-core-util-symbol-type-to-string arg-symbol) (dojo-symbol-name arg-symbol) (js2-node-abs-pos arg-node) (js2-node-abs-end arg-node))) (let* ((ret-symbol (dojo-js-assign-process-call-node scopes class (1+ level) arg-node))) (dojo-js-assign-set-type-or-register-assignment ret-symbol arg-symbol class (1+ level)))) (defun dojo-js-assign-populate-name-argument-symbol (scopes class level arg-symbol arg-node) (log-assign level (format "Called dojo-js-assign-populate-name-argument-symbol for symbol [%s %s %s]" (dojo-symbol-id arg-symbol) (dojo-core-util-symbol-type-to-string arg-symbol) (dojo-symbol-name arg-symbol))) (let* ((symbol (dojo-scopes-get-node-symbol scopes (1+ level) arg-node t class))) (if (null symbol) (log-assign (1+ level) "Did not find symbol for name-node") (log-assign (1+ level) (format "Derived symbol [%s %s %s]" (dojo-symbol-id symbol) (dojo-core-util-symbol-type-to-string symbol) (dojo-symbol-name symbol))) (dojo-js-assign-set-type-or-register-assignment symbol arg-symbol class (1+ level))))) (defun dojo-js-assign-populate-argument-symbol-with-type (scopes class level arg-symbol arg-node desired-type) (log-assign level (format "Populating symbol [%s %s %s] with type [%s]" (dojo-symbol-id arg-symbol) (dojo-core-util-symbol-type-to-string arg-symbol) (dojo-symbol-name arg-symbol) desired-type)) (dojo-symbol-set-type arg-symbol desired-type t)) (defun dojo-js-assign-process-special-function-call (scopes class level function-symbol call-node) "For some well-known function calls, the extraction code wants to do something special with. For example, a call to lang.extend is supposed to add additional symbols to the this-symbol. Such special cases and tasks are detected and handled in this function. The function returns a list of two booleans (process-arguments process-return-type). They indicate wether the calling function inspection code should process arguments and return type of the function, or wether this task was already performed by this function / is unnecessary." (log-assign level (format "Called dojo-js-assign-process-special-function-call for fct [%s], will handle special function tasks" (dojo-core-util-symbol-to-short-string function-symbol))) (log-assign level (format "... function-parent is [%s]" (dojo-symbol-parent-id function-symbol))) (let* ((process-arguments t) (process-return-type t)) (let* ((fct-name (dojo-symbol-name function-symbol)) (parent-symbol (if (or (string= fct-name "mixin") (string= fct-name "extend")) (dojo-symbol-get-parent dojo-current-workspace function-symbol) nil)) (parent-symbol-object-type (if parent-symbol (dojo-symbol-get-object-object-type parent-symbol) nil)) (parent-is-api-symbol (if parent-symbol (dojo-symbol-is-api-symbol parent-symbol) nil)) (grandparent-id (if (eq parent-symbol-object-type 'DOJO-OBJECTTYPE-STATIC) (dojo-symbol-parent-id parent-symbol) nil)) (defining-class (if grandparent-id (if parent-is-api-symbol ; This (without calling dojo-core-load-load-api-by-resource with a resource ; we don't have at this point) works for our purpose because that missing call ; will be triggered when processing the base symbol (e.g. the "lang" in "lang.mixin") ; beforehand anyway. (gethash grandparent-id (dojo-workspace-id-to-api-class dojo-current-workspace)) (dojo-core-api-get-class-by-id dojo-current-workspace grandparent-id) nil) nil)) (defining-class-path (if defining-class (dojo-class-path defining-class) nil))) (log-assign level (format "fct-name [%s], parent-symbol is %s" fct-name (dojo-core-util-symbol-to-short-string parent-symbol))) (log-assign level (format "parent-symbol-object-type [%s], grandparent-id [%s]" parent-symbol-object-type grandparent-id)) (log-assign level (format "defining-class [%s], defining-class-path [%s]" (if defining-class (dojo-class-id defining-class) "---") defining-class-path)) (cond ((and (string= defining-class-path "dojo/_base/lang") (or (string= fct-name "mixin") (string= fct-name "extend"))) (log-assign (1+ level) (format "Detected case dojo/_base/lang.extend, will process it...")) (dojo-js-assign-process-lang-extend-call scopes class (1+ level) function-symbol call-node) (setq process-arguments nil) (setq process-return-type nil)))) ; (log-assign level (format "... Done; process-args [%s], process-return-type [%s]" process-arguments process-return-type)) (list process-arguments process-return-type))) (defun dojo-js-assign-process-lang-extend-call (scopes class level function-symbol call-node) (let* ((arg-nodes (js2-call-node-args call-node)) (base-arg-node (nth 0 arg-nodes)) (base-symbol (dojo-js-assign-process-statement-node scopes class level base-arg-node)) (base-this-symbol (dojo-core-util-get-this-symbol scopes level base-symbol)) ; (class-name (if (and class-arg-node (dojo-has-node-type target-node "js2-name-node")) (dojo-get-name-from-name-node class-arg-node) nil)) (object-arg-node (nth 1 arg-nodes)) (object-symbol (dojo-js-assign-process-statement-node scopes class (1+ level) object-arg-node))) ; (object-symbol (if (dojo-has-node-type object-arg-node "js2-object-node") ; (dojo-js-assign-process-statement-node scopes class (1+ level) object-arg-node) ; nil))) (cond ((null base-this-symbol) (log-assign level (format "dojo-js-assign-process-lang-extend-call found no base-this-symbol for function-symbol %s" (dojo-core-util-symbol-to-short-string function-symbol)))) ((null object-symbol) (log-assign level (format "dojo-js-assign-process-lang-extend-call found no object-symbol for function-symbol %s" (dojo-core-util-symbol-to-short-string function-symbol)))) ((dojo-core-util-is-object-symbol object-symbol) (log-assign level (format "dojo-js-assign-process-lang-extend-call found base-this-symbol %s and object-symbol %s" (dojo-core-util-symbol-to-short-string base-this-symbol) (dojo-core-util-symbol-to-short-string object-symbol))) (dojo-core-util-mixin-object-symbol base-this-symbol object-symbol)) (t (log-assign level (format "[WARNING] object argument of lang.mixin / lang.extend call is no object-symbol: %s" (dojo-core-util-symbol-to-short-string object-symbol))))))) (defun dojo-js-assign-get-call-ref-context (call-node) (let* ((arg-nodes (js2-call-node-args call-node)) (call-values ())) (dolist (arg-node arg-nodes) (cond ((dojo-has-node-type arg-node "js2-string-node") (push (dojo-get-string-from-string-node arg-node) call-values)) (t (push nil call-values)))) (nreverse call-values))) (defun dojo-js-assign-get-call-ref-index (arg-symbols symbol) (let* ((ref-index nil) (symbol-class-id (dojo-symbol-class-id symbol)) (symbol-id (dojo-symbol-id symbol)) (curr-index 0)) (dolist (arg-symbol arg-symbols) (let* ((arg-symbol-class-id (dojo-symbol-class-id arg-symbol)) (arg-symbol-id (dojo-symbol-id arg-symbol))) (if (and (= symbol-class-id arg-symbol-class-id) (= symbol-id arg-symbol-id)) (setq ref-index curr-index))) (incf curr-index)) ref-index)) (defun dojo-js-assign-follow-call-ref-symbol (level ref-symbol arg-symbols call-context) (log-assign level (format "Called follow-call-ref-symbol with ref-symbol %s, arg-symbols %s, call-context [%s]" (dojo-core-util-symbol-to-short-string ref-symbol) (dojo-core-util-symbol-to-short-string arg-symbols) call-context)) (let* ((ref-type (dojo-symbol-get-ref-type ref-symbol)) (ref-symbols (dojo-symbol-get-ref-symbols ref-symbol))) (cond ((eq ref-type 'DOJO-REFTYPE-OBJECT-MEMBER) (let* ((curr-symbol (nth 0 ref-symbols)) (ref-index 1) (stop nil)) (while (and (not stop) (< ref-index (length ref-symbols))) (log-assign level (format "... At ref-index [%s]: curr-symbol %s" ref-index (dojo-core-util-symbol-to-short-string curr-symbol))) (let* ((next-symbol (nth ref-index ref-symbols))) (cond ((null curr-symbol) (log-assign level (format "[WARNING] follow-ref-symbol found (null curr-symbol) at ref-index [%s] for ref-symbol [%s]" ref-index (dojo-core-util-symbol-to-short-string ref-symbol))) (setq stop t)) ((not (dojo-symbol-p next-symbol)) (log-assign level (format "[WARNING] follow-ref-symbol found next-symbol which is no dojo-symbol; possibly something like '*', will stop here."))) ((dojo-core-util-is-object-symbol curr-symbol) (let* ((arg-index (dojo-js-assign-get-call-ref-index arg-symbols next-symbol))) (log-assign level (format "... Case curr-symbol is object: arg-index is [%s], next-symbol %s" arg-index (dojo-core-util-symbol-to-short-string next-symbol))) (if (and (not (null arg-index)) (not (null (nth arg-index call-context)))) (setq curr-symbol (dojo-symbol-get-object-member (nth arg-index call-context) curr-symbol)) (setq stop t)))) (t (log-assign level (format "[WARNING] follow-call-ref-symbol found unsupported case at curr-symbol [%s]" (dojo-core-util-symbol-to-short-string curr-symbol)))))) (incf ref-index)) (log-assign level (format "... Returning curr-symbol %s" (dojo-core-util-symbol-to-short-string curr-symbol))) (log-assign level (format "... object-type [%s], object-info [%s]" (if (and curr-symbol (dojo-core-util-is-object-symbol curr-symbol)) (dojo-symbol-get-object-object-type curr-symbol) "---") (if (and curr-symbol (dojo-core-util-is-object-symbol curr-symbol)) (dojo-symbol-get-object-object-info curr-symbol) "---"))) curr-symbol)) (t (log-assign level (format "[WARNING] follow-ref-symbol found unsupported ref-type [%s]" ref-type)))))) (defun dojo-js-assign-register-scope-in-parent-scope (scopes name scope) (let* ((parent-scope (nth 0 scopes)) (parent-scope-contents (dojo-scope-name-to-symbol parent-scope))) (puthash (concat "=scope=" name) scope parent-scope-contents))) (defun dojo-js-assign-set-type-or-register-assignment (source dest class level &optional always-register-assignment) (cond (; This happens e.g. if a null value was parsed. As a null value ; gives as no useful type information at all, we simply ignore it. (or (null source) (null dest)) ()) ; Source and dest are dojo-symbols. If they are in the same class, ; and the source type is already known, we propagate the type ; information from source to dest. ; If they are in different classes, or the type information isn't ; already known, we register an appropriate assignment. ((and (dojo-symbol-p source) (dojo-symbol-p dest)) (let* ((source-type (dojo-symbol-type source)) (source-class-id (dojo-symbol-class-id source)) (dest-class-id (dojo-symbol-class-id dest))) ; Copy information from source to dest. ; Compared with only analyzing the assignment graph at the very end, ; this has the benefit that type information is known, and will be ; propagated earlier. This should at least make analyzing debug output ; easier. (if (and source-type (eq source-class-id dest-class-id)) (progn (dojo-symbol-set-type dest source-type t t) (dojo-core-util-copy-additional-attributes source dest t))) ; Always register an assignment. ; We will step through the whole assignment graph at the end, to ensure ; that we actually propagated all information we have. (dojo-js-assign-register-assignment class source dest))) ; Now we know, that neither source nor dest is null, and that not ; both of them are symbols. ; Thus, if dest is a dojo-symbol, source is a raw type, i.e. we can just ; set the type of dest accordingly. ((dojo-symbol-p dest) (dojo-symbol-set-type dest source t t)) ; The other way round, if source is a dojo-symbol, dest is a raw type. ((dojo-symbol-p source) (log-assign level (format "[WARNING] Case source [%s] is symbol, and dest [%s] is raw type is triggered. Implement it." (dojo-symbol-id source) dest))) (t (log-assign level (format "[WARNING] Case source [%s] and dest [%s] is raw type is triggered. Nothing useful can be done here." source dest))))) (defun dojo-js-assign-register-assignment (class source dest) (let* ((source-symbol-id (dojo-symbol-id source)) (source-class-id (dojo-symbol-class-id source)) (dest-symbol-id (dojo-symbol-id dest)) (dest-class-id (dojo-symbol-class-id dest)) (in-assignment-map (dojo-class-in-assignment-map class)) (out-assignment-map (dojo-class-out-assignment-map class)) (source-list (list source-class-id source-symbol-id)) (dest-list (list dest-class-id dest-symbol-id)) (in-assignment-value-map (gethash dest-list in-assignment-map)) (out-assignment-value-map (gethash source-list out-assignment-map))) (log-assign 0 (format "REGISTER ASSIGNMENT: (%s:%s) --> (%s:%s)" source-class-id source-symbol-id dest-class-id dest-symbol-id)) (if (null in-assignment-value-map) (progn (setq in-assignment-value-map (make-hash-table :test 'equal)) (puthash dest-list in-assignment-value-map in-assignment-map))) (puthash source-list t in-assignment-value-map) (if (null out-assignment-value-map) (progn (setq out-assignment-value-map (make-hash-table :test 'equal)) (puthash source-list out-assignment-value-map out-assignment-map))) (puthash dest-list t out-assignment-value-map))) (defun dojo-js-assign-propagate-symbol-type (source-symbol dest-symbol level) (log-assign level (format "Propagating type [%s] from source-symbol [%s] to dest-symbol [%s]" (dojo-symbol-type source-symbol) (dojo-symbol-id source-symbol) (dojo-symbol-id dest-symbol))) (let ((type (dojo-symbol-type source-symbol))) ; Set the type... (dojo-symbol-set-type dest-symbol type) ; ... and propagate any additional information. (cond ((eq type 'DOJO-JSTYPE-ARRAY) (dojo-symbol-set-array-value-type dest-symbol (dojo-symbol-get-array-value-type source-symbol))) ((or (eq type 'DOJO-JSTYPE-ARRAY-OR-OBJECT) (eq type 'DOJO-JSTYPE-OBJECT)) (dojo-symbol-set-object-key-type dest-symbol (dojo-symbol-get-object-key-type source-symbol)) (dojo-symbol-set-object-value-type dest-symbol (dojo-symbol-get-object-value-type dest-symbol))) ; Don't propagate the object-members, at least for now ((eq type 'DOJO-JSTYPE-FUNCTION) (dojo-symbol-set-function-arguments dest-symbol (dojo-symbol-get-function-arguments source-symbol)) (dojo-symbol-set-function-return-type dest-symbol (dojo-symbol-get-function-return-type source-symbol)) (dojo-symbol-set-function-builtin-fct dest-symbol (dojo-symbol-get-function-builtin-fct source-symbol))) ; Don't propagate the scope, at least for now ((or (eq type 'DOJO-JSTYPE-IMPORT) (eq type 'DOJO-JSTYPE-INSTANCE)) (dojo-symbol-set-import-resource-id dest-symbol (dojo-symbol-get-import-resource-id source-symbol)) (dojo-symbol-set-import-type dest-symbol (dojo-symbol-get-import-type source-symbol)) (dojo-symbol-set-import-path dest-symbol (dojo-symbol-get-import-path source-symbol)))))) (defun dojo-js-assign-merge-symbols (symbol-one symbol-two) (cond ((null symbol-one) symbol-two) ((null symbol-two) symbol-one) (t ; TODO Actually merge symbols symbol-one))) ;(defun dojo-js-assign-log-assignments () ; (let* ((dirty-symbol-ids (hash-table-get-all-keys (dojo-workspace-dirty-symbol-ids dojo-current-workspace))) ; (out-assignment-map (dojo-workspace-out-assignment-map dojo-current-workspace)) ; (out-assignment-source-symbol-ids (hash-table-get-all-keys out-assignment-map))) ; (log-assign 0 "===== Dirty symbols =====") ; (log-assign 0 "=========================") ;; (let ((id-to-symbol (dojo-workspace-id- to-symbol dojo-current-workspace))) ;; (log-assign 0 (format "%s" (hash-table-get-all-keys id-to-symbol)))) ; ; (dolist (dirty-symbol-id dirty-symbol-ids) ; (let ((dirty-symbol (dojo-workspace-get- symbol-by-id dojo-current-workspace dirty-symbol-id))) ; (log-assign 1 (format "- [%s] %s" dirty-symbol-id (dojo-core-util-symbol-to-short-string dirty-symbol))))) ;; (log-assign 1 (format "- [%s] %s" dirty-symbol-id dirty-symbol)))) ; (log-assign 0 "==== Out Assignments ====") ; (log-assign 0 "=========================") ; (dolist (out-assignment-source-symbol-id out-assignment-source-symbol-ids) ; (let* ((out-assignment-source-symbol (dojo-workspace-get- symbol-by-id dojo-current-workspace out-assignment-source-symbol-id)) ; (out-assignments (gethash out-assignment-source-symbol-id (dojo-workspace-out-assignment-map dojo-current-workspace)))) ; (log-assign 1 (format "%s" (dojo-core-util-symbol-to-short-string out-assignment-source-symbol))) ; (dolist (out-assignment out-assignments) ; (let* ((source-symbol-id (dojo-assignment-source-symbol-id out-assignment)) ; (dest-symbol-id (dojo-assignment-dest-symbol-id out-assignment)) ; (source-symbol (dojo-workspace-get- symbol-by-id dojo-current-workspace source-symbol-id)) ; (dest-symbol (dojo-workspace-get- symbol-by-id dojo-current-workspace dest-symbol-id)) ; (source-symbol-name (if source-symbol (dojo-symbol-name source-symbol) "---")) ; (source-symbol-type (if source-symbol (dojo-symbol-type source-symbol) "---")) ; (dest-symbol-name (if dest-symbol (dojo-symbol-name dest-symbol) "---")) ; (dest-symbol-type (if dest-symbol (dojo-symbol-type dest-symbol) "---"))) ; (log-assign 2 (format "==> [%s %s %s]" dest-symbol-id dest-symbol-type dest-symbol-name)))))))) (defun dojo-js-assign-propagate-types-along-assignments (class) (log-assign 0 (format "Called propagate-types-along-assignments for class with id [%s], is-api [%s], project [%s], path [%s]" (dojo-class-id class) (dojo-class-is-api-class class) (dojo-class-project class) (dojo-class-path class))) (let* ((class-id (dojo-class-id class)) (in-assignment-map (dojo-class-in-assignment-map class)) (out-assignment-map (dojo-class-out-assignment-map class)) (in-count-map (dojo-js-assign-get-in-count-map in-assignment-map out-assignment-map)) (stop nil) (iteration 1)) (maphash (lambda (source-list dest-map) (maphash (lambda (dest-list something) (log-assign 0 (format "Out-assignment %s --> %s" source-list dest-list))) dest-map)) out-assignment-map) (maphash (lambda (dest-list source-map) (maphash (lambda (source-list something) (log-assign 0 (format "In-Assignment %s <-- %s" dest-list source-list))) source-map)) in-assignment-map) (while (not stop) (let* ((in-count-list (dojo-js-assign-get-in-count-list in-count-map)) (processed-count 0)) (log-assign 0 (format "Iteration [%s]: in-count-list %s" iteration in-count-list)) ; At least one in-count entry left, and the very first of the (sorted) list is > 0. (cond ((and (> (length in-count-list) 0) (> (nth 1 (nth 0 in-count-list)) 0)) (log-assign 0 (format "[WARNING] Current in-counts only contain counts > 0, found some not yet supported cycle.")) (setq stop t)) (t (dolist (in-count in-count-list) (let* ((source-list (nth 0 in-count)) (source-symbol (dojo-js-assign-get-symbol-by-class-symbol-list class source-list)) (dest-map (gethash source-list out-assignment-map)) (count (nth 1 in-count))) (cond ((> count 0) (return nil)) ((null source-symbol) (log-assign 0 (format "[WARNING] Source-symbol for source-list %s not found." source-list))) (t (maphash (lambda (dest-list something) (log-assign 1 (format "Processing %s --> %s" source-list dest-list)) (let* ((dest-in-count (gethash dest-list in-count-map)) (source-type (dojo-symbol-type source-symbol)) (dest-class-id (nth 0 dest-list)) (dest-symbol (dojo-js-assign-get-symbol-by-class-symbol-list class dest-list))) (if (null dest-symbol) (log-assign 0 (format "[WARNING] Dest-symbol for dest-list %s not found." dest-list)) (log-assign 1 (format "Processing assignment [%s]:%s to [%s]:%s" source-list (dojo-core-util-symbol-to-short-string source-symbol) dest-list (dojo-core-util-symbol-to-short-string dest-symbol))) (if (= class-id dest-class-id) (progn (log-assign 2 (format "dest-symbol is in our class.")) (dojo-js-api-merge-api-symbol class dest-symbol source-symbol nil nil) (log-assign 2 (format "... dest-symbol is in our class: Done."))) ; (dojo-symbol-set-type dest-symbol source-type t t) ; (dojo-core-util-copy-additional-attributes source-symbol dest-symbol t)) (log-assign 2 (format "dest-symbol is in the api-class.")) (dojo-js-api-merge-api-symbol (gethash dest-class-id (dojo-workspace-id-to-api-class dojo-current-workspace)) dest-symbol source-symbol nil nil))) (incf processed-count) (if (not (null dest-in-count)) (progn (log-assign 3 (format "Decrementing for %s to [%s]" dest-list (1- dest-in-count))) (puthash dest-list (1- dest-in-count) in-count-map))))) dest-map) (log-assign 1 (format "Deleting entry for %s --> [%s] from in-count-map." source-list (gethash source-list in-count-map))) (remhash source-list in-count-map) )))))) (if (= processed-count 0) (progn (log-assign 0 (format "Iteration [%s]: Processed nothing, will quit due to missing progress." iteration)) (setq stop t)) (log-assign 0 (format "Iteration [%s]: Copied data from source to dest symbol [%s] times." iteration processed-count))) (incf iteration))) (log-assign 0 (format "Finished with propagate-types-along-assignments after [%s] iterations." (1- iteration))))) (defun dojo-js-assign-get-in-count-map (in-assignment-map out-assignment-map) (let* ((in-counts (make-hash-table :test 'equal))) (maphash (lambda (out-source-list something) (let* ((in-source-map (gethash out-source-list in-assignment-map))) (puthash out-source-list (dojo-common-containers-get-hashtable-size in-source-map) in-counts))) out-assignment-map) in-counts)) (defun dojo-js-assign-get-in-count-list (in-count-map) (let* ((in-count-list ())) (maphash (lambda (out-source-list count) (push (list out-source-list count) in-count-list)) in-count-map) (setq in-count-list (sort in-count-list (lambda (in-count-one in-count-two) (< (nth 1 in-count-one) (nth 1 in-count-two))))) in-count-list)) (defun dojo-js-assign-get-symbol-by-class-symbol-list (base-class class-symbol-list) (let* ((class-id (nth 0 class-symbol-list)) (symbol-id (nth 1 class-symbol-list)) (class (if (= class-id (dojo-class-id base-class)) base-class (gethash class-id (dojo-workspace-id-to-api-class dojo-current-workspace)))) (id-to-symbol (if class (dojo-class-id-to-symbol class) nil)) (symbol (if id-to-symbol (gethash symbol-id id-to-symbol) nil))) (log-assign 2 (format "get-symbol-by-class-symbol-list for base-class [%s:%s:%s:%s] uses class [%s:%s:%s:%s] for symbol-list [%s]; symbol is %s" (dojo-class-id base-class) (dojo-class-project base-class) (dojo-class-path base-class) (dojo-class-is-api-class base-class) (dojo-class-id class) (dojo-class-project class) (dojo-class-path class) (dojo-class-is-api-class class) class-symbol-list (dojo-core-util-symbol-to-short-string symbol))) symbol)) (defun dojo-js-assign-log-symbol-long (level symbol &optional dirty-ids-passed prefix) (cond ((null symbol) (log-assign level (format "%s[nil]" (if prefix (concat prefix " ") "") symbol))) ((not (dojo-symbol-p symbol)) (log-assign level (format "%s[non-symbol [%s]]" (if prefix (concat prefix " ") "") symbol))) (t (let* ((id (dojo-symbol-id symbol)) (name (dojo-symbol-name symbol)) (raw-type (if (dojo-symbol-p symbol) (dojo-symbol-type symbol) nil)) (type (if (dojo-symbol-p symbol) (dojo-core-util-symbol-type-to-string symbol) symbol)) (dirty-ids (if dirty-ids-passed dirty-ids-passed (make-hash-table :test 'equal)))) (log-assign level (format "%s[%s %s %s]" (if prefix (concat prefix " ") "") id name type)) ; Avoid endless recursion in the logging code for sure. So print the details of each symbol ; exactly once. (if (gethash id dirty-ids) (log-assign (1+ level) (format "[Symbol already printed, thus omitting details here]")) (progn (puthash id t dirty-ids) (cond ((eq raw-type 'DOJO-JSTYPE-ARRAY) (let ((value-type (dojo-symbol-get-array-value-type symbol))) (dojo-js-assign-log-symbol-long (1+ level) value-type dirty-ids "Value type"))) ((eq raw-type 'DOJO-JSTYPE-FUNCTION) (let* ((arguments (dojo-symbol-get-function-arguments symbol)) (return-type (dojo-symbol-get-function-return-type symbol))) (log-assign (1+ level) (format "Arguments:")) (dolist (argument arguments) (dojo-js-assign-log-symbol-long (1+ (1+ level)) argument dirty-ids "-")) (dojo-js-assign-log-symbol-long (1+ level) return-type dirty-ids "Returns"))) ((or (eq raw-type 'DOJO-JSTYPE-IMPORT) (eq raw-type 'DOJO-JSTYPE-INSTANCE)) (let ((resource-id (dojo-symbol-get-import-resource-id symbol)) (type (dojo-symbol-get-import-type symbol)) (path (dojo-symbol-get-import-path symbol))) (log-assign (1+ level) (format "Resource [%s], Type [%s], Path [%s]" resource-id type path)))) ((or (eq raw-type 'DOJO-JSTYPE-ARRAY-OR-OBJECT) (eq raw-type 'DOJO-JSTYPE-OBJECT)) (let ((key-type (dojo-symbol-get-object-key-type symbol)) (value-type (dojo-symbol-get-object-value-type symbol)) (name-to-symbol (dojo-symbol-get-object-members symbol))) (dojo-js-assign-log-symbol-long (1+ level) key-type dirty-ids "Key type") (dojo-js-assign-log-symbol-long (1+ level) value-type dirty-ids "Value type") (log-assign (1+ level) "Members:") (dolist (name (hash-table-get-all-keys name-to-symbol)) (dojo-js-assign-log-symbol-long (1+ (1+ level)) (gethash name name-to-symbol) dirty-ids (format "- %s" name)))))))))))) (defun dojo-js-assign-log-scope (level scope) (if (or (null scope) (not (dojo-scope-p scope))) (log-assign level (format "[WARNING] Passed scope isn't a scope, but %s" scope)) (let* ((scope-level (dojo-scope-level scope)) (description (dojo-scope-description scope)) (name-to-symbol (dojo-scope-name-to-symbol scope))) (log-assign level (format "- Scope %s: %s" scope-level description)) (dolist (name (hash-table-get-all-keys name-to-symbol)) (let* ((value (gethash name name-to-symbol))) (cond ((dojo-symbol-p value) (log-assign (1+ level) (format "- %s: %s" name (dojo-core-util-symbol-to-short-string value)))) ((dojo-scope-p value) (dojo-js-assign-log-scope (1+ level) value)) (t (log-assign (1+ level) (format "- unknown value: [%s]" value))))))))) (provide 'dojo-js-assign)