com.google.javascript.jscomp.TypedVar Java Examples

The following examples show how to use com.google.javascript.jscomp.TypedVar. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DeclarationGenerator.java    From clutz with MIT License 6 votes vote down vote up
/**
 * Finds all typedefs in the program and build a Type -> typedef name mapping. The mapping is
 * needed because when walking type definitions closure inlines the typedefs values.
 */
void collectTypedefs() {
  for (TypedVar var : compiler.getTopScope().getAllSymbols()) {
    // In Closure, unlike TypeScript there is no pure type space. Thus even typedefs declare
    // symbols. The type of the symbol corresponding to the typedef is *not* the same as the type
    // declared by the typedef.
    JSType type = var.getType();
    if (type == null
        || !isTypedef(type)
        || var.getName().startsWith("window.")
        || isPrivate(var.getJSDocInfo())) {
      continue;
    }

    JSType realType = compiler.getTypeRegistry().getGlobalType(var.getName());
    if (realType != null
        && shouldEmitTypedefByName(realType)
        && !typedefs.containsKey(realType)
        && !PlatformSymbols.TYPESCRIPT_LIB_D_TS.contains(var.getName())
        && !PlatformSymbols.CLOSURE_EXTERNS_NOT_USED_IN_TYPESCRIPT.contains(var.getName())) {
      typedefs.put(realType, var.getName());
    }
  }
}
 
Example #2
Source File: NodeModulePassTest.java    From js-dossier with Apache License 2.0 6 votes vote down vote up
@Test
public void canReferenceConstructorDefinedInTheGlobalScope() {
  CompilerUtil compiler = createCompiler(path("x/bar.js"));

  compiler.compile(
      createSourceFile(path("x/foo.js"), "/** @constructor */", "function Foo() {}"),
      createSourceFile(
          path("x/bar.js"), "/** @type {function(new: Foo)} */", "exports.Foo = Foo;"));

  TypedScope scope = compiler.getCompiler().getTopScope();
  TypedVar var = scope.getVar("module$exports$module$x$bar");

  JSType type = var.getInitialValue().getJSType().findPropertyType("Foo");
  assertTrue(type.isConstructor());

  type = type.toObjectType().getTypeOfThis();
  assertEquals("Foo", type.toString());
}
 
Example #3
Source File: NodeModulePassTest.java    From js-dossier with Apache License 2.0 6 votes vote down vote up
@Test
public void canReferenceConstructorExportedByAnotherModule() {
  CompilerUtil compiler = createCompiler(path("x/foo.js"), path("x/bar.js"));

  compiler.compile(
      createSourceFile(path("x/foo.js"), "/** @constructor */", "exports.Foo = function(){};"),
      createSourceFile(
          path("x/bar.js"),
          "var foo = require('./foo');",
          "/** @type {function(new: foo.Foo)} */",
          "exports.Foo = foo.Foo;"));

  TypedScope scope = compiler.getCompiler().getTopScope();
  TypedVar var = scope.getVar("module$exports$module$x$bar");

  JSType type = var.getInitialValue().getJSType().findPropertyType("Foo");
  assertTrue(type.isConstructor());

  type = type.toObjectType().getTypeOfThis();
  assertEquals("module$exports$module$x$foo.Foo", type.toString());
}
 
Example #4
Source File: DeclarationGenerator.java    From clutz with MIT License 6 votes vote down vote up
private boolean needsAlias(Set<String> shadowedSymbols, String provide, TypedVar symbol) {
  if (!shadowedSymbols.contains(provide)) {
    return false;
  }
  // Emit var foo : any for provided but not declared symbols.
  if (symbol == null) {
    return true;
  }
  JSType type = symbol.getType();
  if (type == null) {
    return false;
  }

  // Emit var foo : PrivateType for private symbols.
  if (isPrivate(type.getJSDocInfo()) && !isConstructor(type.getJSDocInfo())) {
    return true;
  }
  // Only var declarations have collisions, while class, interface, function, and typedef can
  // coexist with namespaces.
  if (type.isInterface() || type.isConstructor() || type.isFunctionType() || isTypedef(type)) {
    return false;
  }
  return isDefaultExport(symbol);
}
 
Example #5
Source File: NodeModulePassTest.java    From js-dossier with Apache License 2.0 5 votes vote down vote up
@Test
public void canUseModuleInternalTypedefsInJsDoc() {
  CompilerUtil compiler = createCompiler(path("foo.js"));

  compiler.compile(
      createSourceFile(
          path("foo.js"),
          "/** @typedef {{x: number}} */",
          "var Variable;",
          "",
          "/**",
          " * @param {Variable} a .",
          " * @param {Variable} b .",
          " * @return {Variable} .",
          " */",
          "exports.add = function(a, b) {",
          "  return {x: a.x + b.x};",
          "};"));

  TypedScope scope = compiler.getCompiler().getTopScope();
  TypedVar var = scope.getVar("module$exports$module$foo");
  JSType type = var.getInitialValue().getJSType().toObjectType().getPropertyType("add");
  assertTrue(type.isFunctionType());

  JSDocInfo info = type.getJSDocInfo();
  Node node = info.getTypeNodes().iterator().next();
  assertTrue(node.isString());
  assertEquals("module$contents$module$foo_Variable", node.getString());
  assertEquals("Variable", node.getProp(Node.ORIGINALNAME_PROP));
}
 
Example #6
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
/**
 * Precompute the list of children symbols for all top-scope symbols.
 *
 * <p>I.e. For each x.y -> [x.y.z, x.y.w]
 */
void precomputeChildLists() {
  for (TypedVar var : compiler.getTopScope().getAllSymbols()) {
    String namespace = getNamespace(var.getName());
    if (!namespace.equals("")) {
      childListMap.put(namespace, var);
    }
  }
}
 
Example #7
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
/**
 * Special emit for emitting namespaces for typedefs as one-offs. Typedefs (type aliases in TS)
 * are special because their information does not come solely from a TypedVar object, but rather
 * from a pair of name (string) and type (JSType).
 *
 * <p>Note that this only handles default exports of typedefs. Typedefs as properties on an
 * already exported object (like class) are handled separately.
 */
private void declareTypedefNamespace(TypedVar typedef, JSType typedefType, Set<String> provides) {
  String typedefName = typedef.getName();
  String namespace = getNamespace(typedefName);

  emitGeneratedFromFileComment(typedef.getSourceFile());
  // Ideally we should be using declareNamespace here, but it cannot handle gluing the TypedVar
  // symbol with the additional JSType typedefType.
  emitNamespaceBegin(namespace);

  new TreeWalker(compiler.getTypeRegistry(), provides, false, false)
      .visitTypeAlias(typedefType, typedefName, false);

  emitNamespaceEnd();
}
 
Example #8
Source File: NodeModulePassTest.java    From js-dossier with Apache License 2.0 5 votes vote down vote up
@Test
public void savesOriginalTypeNameInJsDoc() {
  CompilerUtil compiler = createCompiler(path("foo.js"));

  compiler.compile(
      createSourceFile(
          path("foo.js"),
          "/** @constructor */",
          "var Builder = function(){};",
          "/** @return {!Builder} . */",
          "Builder.prototype.returnThis = function() { return this; };",
          "exports.Builder = Builder"));

  TypedScope scope = compiler.getCompiler().getTopScope();
  TypedVar var = scope.getVar("module$exports$module$foo");
  JSType type = var.getInitialValue().getJSType().findPropertyType("Builder");
  assertTrue(type.isConstructor());

  type = type.toObjectType().getTypeOfThis();
  assertEquals("module$exports$module$foo.Builder", type.toString());

  type = type.toObjectType().getPropertyType("returnThis");
  assertTrue(type.toString(), type.isFunctionType());

  JSDocInfo info = type.getJSDocInfo();
  assertNotNull(info);

  Node node = getOnlyElement(info.getTypeNodes());
  assertEquals(Token.BANG, node.getToken());

  node = node.getFirstChild();
  assertTrue(node.isString());
  assertEquals("module$exports$module$foo.Builder", node.getString());
  assertEquals("Builder", node.getProp(Node.ORIGINALNAME_PROP));
}
 
Example #9
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
/**
 * If any inputs declare a legacy namespace, emit aliases for their exports in goog.module style.
 */
private void declareLegacyNamespaceAliases() {
  if (!legacyNamespaceReexportMap.isEmpty()) {
    for (Entry<String, String> e : legacyNamespaceReexportMap.entrySet()) {
      String namespace;
      String googModuleStyleName;
      if (e.getKey().contains(".")) {
        List<String> nameParts = DOT_SPLITTER.splitToList(e.getKey());
        namespace = nameParts.get(0);
        googModuleStyleName = nameParts.get(1);
      } else {
        namespace = "";
        googModuleStyleName = e.getKey();
      }
      TreeWalker treeWalker =
          new TreeWalker(compiler.getTypeRegistry(), new LinkedHashSet<>(), false, false);
      TypedVar symbol = compiler.getTopScope().getOwnSlot(e.getValue());
      if (symbol != null) {
        JSType type = symbol.getType();
        if (type != null && isDefiningType(type)) {
          emitGeneratedFromFileComment(symbol.getSourceFile());
          emitNamespaceBegin(namespace);
          treeWalker.visitTypeValueAlias(googModuleStyleName, type.toMaybeObjectType());
          emitNamespaceEnd();
        }
      }
    }
  }
}
 
Example #10
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
private boolean isAliasedClassOrInterface(TypedVar symbol, JSType type) {
  // Confusingly typedefs are constructors. However, they cannot be aliased AFAICT.
  if (type.isNoType()) return false;
  if (!type.isConstructor() && !type.isInterface()) return false;
  String symbolName = symbol.getName();
  String typeName = type.getDisplayName();
  // Turns out that for aliases the symbol and type name differ.
  return !symbolName.equals(typeName) || KNOWN_CLASS_ALIASES.containsKey(symbolName);
}
 
Example #11
Source File: AbstractClosureVisitor.java    From jsinterop-generator with Apache License 2.0 5 votes vote down vote up
public void accept(TypedScope scope) {
  pushCurrentJavaType(scope.getTypeOfThis());
  if (visitTopScope(scope)) {
    for (TypedVar symbol : scope.getVarIterable()) {
      if (isDefinedInExternFiles(symbol) && isNotNamespaced(symbol)) {
        accept(symbol, true);
      }
    }
  }
  popCurrentJavaType();
}
 
Example #12
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
/**
 * Extra walk is required for inner classes and inner enums. They are allowed in closure, but not
 * in TS, so we have to generate a namespace-class pair in TS. In the case of the externs, however
 * we *do* go through all symbols so this pass is not needed. In the case of aliased classes, we
 * cannot emit inner classes, due to a var-namespace clash.
 *
 * <p>Similarly, for "namespace" types - i.e. @const on an inferred {}, we cannot safely emit
 * namespace pair.
 */
private void maybeQueueForInnerWalk(
    boolean isExtern,
    Map<String, ObjectType> symbolsToInnerWalk,
    TypedVar propertySymbol,
    String propertyName) {
  ObjectType oType = propertySymbol.getType().toMaybeObjectType();
  if (!isExtern
      && oType != null
      && !isAliasedClassOrInterface(propertySymbol, oType)
      && !isNamespaceType(oType)) {
    symbolsToInnerWalk.put(propertyName, oType);
  }
}
 
Example #13
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
private boolean isPrototypeMethod(TypedVar other) {
  if (other.getType() != null && other.getType().isOrdinaryFunction()) {
    JSType typeOfThis = ((FunctionType) other.getType()).getTypeOfThis();
    if (typeOfThis != null && !typeOfThis.isUnknownType()) {
      return true;
    }
  }
  return false;
}
 
Example #14
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private void visitTypeAlias(JSType registryType, TypedVar symbol) {
  visitTypeAlias(registryType, getUnqualifiedName(symbol), false);
}
 
Example #15
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private static boolean isDefaultExport(TypedVar symbol) {
  if (symbol.getType() == null) return true;
  ObjectType otype = symbol.getType().toMaybeObjectType();
  if (otype != null && otype.getOwnPropertyNames().size() == 0) return true;
  return !isNamespaceType(symbol.getType());
}
 
Example #16
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private void emitSkipTypeAlias(TypedVar symbol) {
  emit(
      "/* skipped emitting type alias "
          + symbol.getName()
          + " to avoid collision with existing one in lib.d.ts. */");
}
 
Example #17
Source File: BuildSymbolTablePassTest.java    From js-dossier with Apache License 2.0 4 votes vote down vote up
@Test
public void googScope() {
  Scenario scenario = new Scenario();
  SymbolTable table =
      scenario
          .addFile(
              stdInput,
              "goog.provide('foo.bar.baz');",
              "goog.scope(function() {",
              "const fbb = foo.bar.baz;",
              "fbb.quot = function() {};",
              "fbb.quot.quux = 123;",
              "fbb.A = class X {};",
              "foo.bar.baz.B = class Y {};",
              "});",
              "foo.bar.baz.end = 1;")
          .compile();

  assertThat(table)
      .containsExactly(
          "foo.bar.baz",
          "foo.bar.baz.quot",
          "foo.bar.baz.quot.quux",
          "foo.bar.baz.A",
          "foo.bar.baz.B",
          "foo.bar.baz.end");

  // Need to find the node for fbb.quot so we can find the symbol table for the block.
  TypedScope scope = scenario.getCompiler().getTopScope();
  TypedVar var = scope.getVar("foo.bar.baz.quot");
  SymbolTable varTable = table.findTableFor(var.getNode());

  assertThat(varTable).isNotSameAs(table);
  assertThat(varTable.getParentScope()).isSameAs(table);
  assertThat(varTable).containsExactly("fbb", "fbb.quot", "fbb.quot.quux", "fbb.A");
  assertThat(varTable).hasOwnSymbol("fbb").that().isAReferenceTo("foo.bar.baz");
  assertThat(varTable).hasOwnSymbol("fbb.quot").that().isAReferenceTo("foo.bar.baz.quot");
  assertThat(varTable)
      .hasOwnSymbol("fbb.quot.quux")
      .that()
      .isAReferenceTo("foo.bar.baz.quot.quux");
  assertThat(varTable).hasOwnSymbol("fbb.A").that().isAReferenceTo("foo.bar.baz.A");
}
 
Example #18
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private String getUnqualifiedName(TypedVar symbol) {
  return getUnqualifiedName(symbol.getName());
}
 
Example #19
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private boolean isPrivate(String name) {
  TypedVar var = compiler.getTopScope().getOwnSlot(name);
  if (var == null) return false;
  return isPrivate(var.getJSDocInfo());
}
 
Example #20
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private void sortSymbols(List<TypedVar> symbols) {
  Collections.sort(symbols, BY_SOURCE_FILE_AND_VAR_NAME);
}
 
Example #21
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
/**
 * Closure does not require all types to be explicitly provided, if they are only used in type
 * positions. However, our emit phases only emits goog.provided symbols and namespaces, so this
 * extra pass is required, in order to have valid output.
 */
private void processUnprovidedTypes(Set<String> provides, Set<String> transitiveProvides) {
  /**
   * A new set of types can be discovered while visiting unprovided types. To prevent an infinite
   * loop in a pathological case, limit to a number of passes.
   *
   * <p>TODO(rado): investigate https://github.com/angular/clutz/pull/246 and removing this pass
   * altogether.
   */
  int maxTypeUsedDepth = 5;
  Set<String> typesEmitted = new LinkedHashSet<>();
  while (maxTypeUsedDepth > 0) {
    int typesUsedCount = typesUsed.size();
    // AFAICT, there is no api for going from type to symbol, so iterate all symbols first.
    for (TypedVar symbol : compiler.getTopScope().getAllSymbols()) {
      String name = symbol.getName();
      String namespace = getNamespace(name);
      // skip unused symbols, symbols already emitted or symbols whose namespace is emitted
      // (unless the symbols have their own provide).
      if (!typesUsed.contains(name)
          || typesEmitted.contains(name)
          || (!transitiveProvides.contains(name) && typesEmitted.contains(namespace))) {
        continue;
      }

      // skip provided symbols (as default or in an namespace).
      if (provides.contains(name)
          || (!transitiveProvides.contains(name) && provides.contains(namespace))) {
        continue;
      }
      // skip emit for provided inner symbols too as they are covered by the walkInnerSymbols
      // pass.
      if (isInnerSymbol(provides, name)) {
        continue;
      }

      // Skip extern symbols (they have a separate pass) and skip built-ins.
      // Built-ins can be indentified by having null as input file.
      CompilerInput symbolInput = this.compiler.getInput(new InputId(symbol.getInputName()));
      if (symbolInput == null || symbolInput.isExtern()) continue;

      // Type inference sometimes creates symbols for undeclared qualified names when narrowing
      // their type in a flow scope. These should not be emitted and can be detected by noticing
      // the declaration 'node' is not used as an lvalue.
      Node nameNode = symbol.getNode();
      if (!NodeUtil.isLValue(nameNode) && !nameNode.getParent().isExprResult()) {
        continue;
      }

      // A symbol with a name, but a null type is likely a typedef. DeclareNamespace cannot handle
      // this scenario, but declareTypedefNamespace
      if (symbol.getType() == null) {
        JSType typedef = compiler.getTypeRegistry().getGlobalType(name);
        if (typedef != null) {
          declareTypedefNamespace(symbol, typedef, Collections.emptySet());
          typesEmitted.add(name);
        }
        continue;
      }

      declareNamespace(
          namespace,
          symbol,
          name,
          /* isDefault */ true,
          Collections.<String>emptySet(),
          /* isExtern */ false);
      typesEmitted.add(name);
    }
    // if no new types seen, safely break out.
    if (typesUsed.size() == typesUsedCount) break;
    maxTypeUsedDepth--;
  }
}
 
Example #22
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
private boolean isArrayLike(TypedVar symbol) {
  return symbol.getName().endsWith(".ArrayLike");
}
 
Example #23
Source File: DeclarationGenerator.java    From clutz with MIT License 4 votes vote down vote up
/**
 * Reserved words are problematic because they cannot be used as var declarations, but are valid
 * properties. For example:
 *
 * <pre>
 * var switch = 0;  // parses badly in JS.
 * foo.switch = 0;  // ok.
 * </pre>
 *
 * This means that closure code is allowed to goog.provide('ng.components.switch'), which cannot
 * trivially translate in TS to:
 *
 * <pre>
 * namespace ng.components {
 *   var switch : ...;
 * }
 * </pre>
 *
 * Instead, go one step higher and generate:
 *
 * <pre>
 * namespace ng {
 *   var components : {switch: ..., };
 * }
 * </pre>
 *
 * This turns a namespace into a property of its parent namespace. Note: this violates the
 * invariant that generated namespaces are 1-1 with getNamespace of goog.provides.
 */
private void processReservedSymbols(TreeSet<String> provides, TypedScope topScope) {
  Set<String> collapsedNamespaces = new TreeSet<>();
  for (String reservedProvide : provides) {
    if (RESERVED_JS_WORDS.contains(getUnqualifiedName(reservedProvide))) {
      TypedVar var = topScope.getOwnSlot(reservedProvide);
      String namespace = getNamespace(reservedProvide);
      if (collapsedNamespaces.contains(namespace)) continue;
      collapsedNamespaces.add(namespace);
      Set<String> properties = getSubNamespace(provides, namespace);
      if (var != null) {
        emitGeneratedFromFileComment(var.getSourceFile());
      }
      emitNamespaceBegin(getNamespace(namespace));
      emit("let");
      emit(getUnqualifiedName(namespace));
      emit(": {");
      Iterator<String> bundledIt = properties.iterator();
      while (bundledIt.hasNext()) {
        emit(getUnqualifiedName(bundledIt.next()));
        emit(":");
        if (var != null) {
          TreeWalker walker = new TreeWalker(compiler.getTypeRegistry(), provides, false, false);
          walker.visitType(var.getType());
        } else {
          emit("any");
        }
        if (bundledIt.hasNext()) emit(",");
      }
      emit("};");
      emitBreak();
      emitNamespaceEnd();
      for (String property : properties) {
        // Assume that all symbols that are siblings of the reserved word are default exports.
        declareModule(property, true, property, true, var != null ? var.getSourceFile() : null);
      }
    }
  }
  // Remove the symbols that we have emitted above.
  Iterator<String> it = provides.iterator();
  while (it.hasNext()) {
    if (collapsedNamespaces.contains(getNamespace(it.next()))) it.remove();
  }
}
 
Example #24
Source File: AbstractClosureVisitor.java    From jsinterop-generator with Apache License 2.0 4 votes vote down vote up
private boolean isDefinedInExternFiles(TypedVar symbol) {
  return externFileNamesSet.contains(symbol.getInputName());
}
 
Example #25
Source File: AbstractClosureVisitor.java    From jsinterop-generator with Apache License 2.0 4 votes vote down vote up
private static boolean isNotNamespaced(TypedVar symbol) {
  return !symbol.getName().contains(".");
}