コード例 #1
0
    public void onRedeclaration(
        Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) {
      // Don't allow multiple variables to be declared at the top level scope
      if (scope.isGlobal()) {
        Scope.Var origVar = scope.getVar(name);
        Node origParent = origVar.getParentNode();
        if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) {
          // Okay, both are 'catch(x)' variables.
          return;
        }

        boolean allowDupe = false;
        JSDocInfo info = n.getJSDocInfo();
        if (info == null) {
          info = parent.getJSDocInfo();
        }
        allowDupe = info != null && info.getSuppressions().contains("duplicate");

        if (!allowDupe) {
          compiler.report(
              JSError.make(
                  sourceName,
                  nodeWithLineNumber,
                  VAR_MULTIPLY_DECLARED_ERROR,
                  name,
                  (origVar.input != null ? origVar.input.getName() : "??")));
        }
      } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) {
        // Disallow shadowing "arguments" as we can't handle with our current
        // scope modeling.
        compiler.report(JSError.make(sourceName, nodeWithLineNumber, VAR_ARGUMENTS_SHADOWED_ERROR));
      }
    }
コード例 #2
0
 private final void typeCheck(String externs, String js, DiagnosticType... warningKinds) {
   parseAndTypeCheck(externs, js);
   JSError[] warnings = compiler.getWarnings();
   JSError[] errors = compiler.getErrors();
   String errorMessage =
       "Expected warning of type:\n"
           + "================================================================\n"
           + Arrays.toString(warningKinds)
           + "================================================================\n"
           + "but found:\n"
           + "----------------------------------------------------------------\n"
           + Arrays.toString(errors)
           + Arrays.toString(warnings)
           + "\n"
           + "----------------------------------------------------------------\n";
   assertEquals(
       errorMessage + "Warning count", warningKinds.length, warnings.length + errors.length);
   for (JSError warning : warnings) {
     assertTrue(
         "Wrong warning type\n" + errorMessage,
         Arrays.asList(warningKinds).contains(warning.getType()));
   }
   for (JSError error : errors) {
     assertTrue(
         "Wrong warning type\n" + errorMessage,
         Arrays.asList(warningKinds).contains(error.getType()));
   }
 }
コード例 #3
0
  /**
   * Sanity checks code generation by performing it once, parsing the result, then generating code
   * from the second parse tree to verify that it matches the code generated from the first parse
   * tree.
   *
   * @return The regenerated parse tree. Null on error.
   */
  private Node sanityCheckCodeGeneration(Node root) {
    if (compiler.hasHaltingErrors()) {
      // Don't even bother checking code generation if we already know the
      // the code is bad.
      return null;
    }

    String source = compiler.toSource(root);
    Node root2 = compiler.parseSyntheticCode(source);
    if (compiler.hasHaltingErrors()) {
      compiler.report(
          JSError.make(
              CANNOT_PARSE_GENERATED_CODE, Strings.truncateAtMaxLength(source, 100, true)));

      // Throw an exception, so that the infrastructure will tell us
      // which pass violated the sanity check.
      throw new IllegalStateException("Sanity Check failed");
    }

    String source2 = compiler.toSource(root2);
    if (!source.equals(source2)) {
      compiler.report(
          JSError.make(
              GENERATED_BAD_CODE,
              Strings.truncateAtMaxLength(source, 1000, true),
              Strings.truncateAtMaxLength(source2, 1000, true)));

      // Throw an exception, so that the infrastructure will tell us
      // which pass violated the sanity check.
      throw new IllegalStateException("Sanity Check failed");
    }

    return root2;
  }
コード例 #4
0
 /**
  * Emits a warning for each default value parameter that has the wrong type and for each getter
  * function that was used for the wrong type of tweak.
  */
 void emitAllTypeWarnings() {
   for (TweakFunctionCall call : functionCalls) {
     Node valueNode = call.valueNode;
     TweakFunction tweakFunc = call.tweakFunc;
     TweakFunction registerFunc = registerCall.tweakFunc;
     if (valueNode != null) {
       // For register* and overrideDefaultValue calls, ensure the default
       // value is a literal of the correct type.
       if (!registerFunc.isValidNodeType(valueNode.getType())) {
         compiler.report(
             JSError.make(
                 valueNode,
                 INVALID_TWEAK_DEFAULT_VALUE_WARNING,
                 tweakId,
                 registerFunc.getName(),
                 registerFunc.getExpectedTypeName()));
       }
     } else if (tweakFunc.isGetterFunction()) {
       // For getter calls, ensure the correct getter was used.
       if (!tweakFunc.isCorrectRegisterFunction(registerFunc)) {
         compiler.report(
             JSError.make(
                 call.callNode,
                 TWEAK_WRONG_GETTER_TYPE_WARNING,
                 tweakFunc.getName(),
                 registerFunc.getName()));
       }
     }
   }
 }
コード例 #5
0
  public void testOrderingCharno2() throws Exception {
    JSError e1 = JSError.make(NULL_SOURCE, 8, 7, FOO_TYPE);
    JSError e2 = JSError.make(NULL_SOURCE, 8, 5, FOO_TYPE);

    assertSmaller(error(e2), error(e1));
    // CheckLevel preempts charno comparison
    assertSmaller(warning(e2), error(e1));
  }
コード例 #6
0
 @Override
 public void println(CheckLevel level, JSError error) {
   switch (level) {
     case ERROR:
       logger.severe(error.format(level, formatter));
       break;
     case WARNING:
       logger.warning(error.format(level, formatter));
       break;
   }
 }
コード例 #7
0
    /** Processes a OBJECTLIT node. */
    private void handleObjectLit(NodeTraversal t, Node n) {
      Node child = n.getFirstChild();
      while (child != null) {
        // Maybe STRING, NUMBER, GET, SET
        if (child.getType() != Token.NUMBER) {
          // We should never see a mix of numbers and strings.
          String name = child.getString();
          T type = typeSystem.getType(getScope(), n, name);

          Property prop = getProperty(name);
          if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) {
            if (showInvalidationWarnings) {
              compiler.report(
                  JSError.make(
                      t.getSourceName(),
                      child,
                      INVALIDATION,
                      name,
                      (type == null ? "null" : type.toString()),
                      n.toString()));
            }
          }
        }

        child = child.getNext();
      }
    }
コード例 #8
0
    private void visitScriptNode() {
      for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) {
        String ctor = ctorEntry.getKey();
        int index = -1;
        boolean found = false;

        if (ctor.startsWith("$jscomp.")) {
          continue;
        }

        do {
          index = ctor.indexOf('.', index + 1);
          String provideKey = index == -1 ? ctor : ctor.substring(0, index);
          if (provides.containsKey(provideKey)) {
            found = true;
            break;
          }
        } while (index != -1);

        if (!found) {
          Node n = ctorEntry.getValue();
          compiler.report(JSError.make(n, checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey()));
        }
      }
      provides.clear();
      ctors.clear();
    }
コード例 #9
0
  /** Add an @this annotation to all functions in the objLit. */
  private void addTypesToFunctions(Node objLit, String thisType) {
    Preconditions.checkState(objLit.isObjectLit());
    for (Node keyNode : objLit.children()) {
      Node value = keyNode.getLastChild();
      if (value != null && value.isFunction()) {
        JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(keyNode.getJSDocInfo());
        fnDoc.recordThisType(
            new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE));
        keyNode.setJSDocInfo(fnDoc.build());
      }
    }

    // Add @this and @return to default property values.
    for (MemberDefinition property : extractProperties(objLit)) {
      if (!property.value.isObjectLit()) {
        continue;
      }
      if (hasShorthandAssignment(property.value)) {
        compiler.report(JSError.make(property.value, POLYMER_SHORTHAND_NOT_SUPPORTED));
        return;
      }

      Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value");
      if (defaultValue == null || !defaultValue.isFunction()) {
        continue;
      }
      Node defaultValueKey = defaultValue.getParent();
      JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(defaultValueKey.getJSDocInfo());
      fnDoc.recordThisType(
          new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE));
      fnDoc.recordReturnType(getTypeFromProperty(property));
      defaultValueKey.setJSDocInfo(fnDoc.build());
    }
  }
コード例 #10
0
 private void checkClassSuperReferences(Node classNode) {
   Node className = classNode.getFirstChild();
   Node superClassName = className.getNext();
   if (NodeUtil.referencesSuper(classNode) && !superClassName.isQualifiedName()) {
     compiler.report(JSError.make(classNode, NO_SUPERTYPE));
   }
 }
コード例 #11
0
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (!n.isAssign() || n.getFirstChild() == className) {
     return;
   }
   if (className.matchesQualifiedName(n.getFirstChild())) {
     compiler.report(JSError.make(n, CLASS_REASSIGNMENT));
   }
 }
コード例 #12
0
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (n.isCall() && isGoogDefineClass(n)) {
     if (!validateUsage(n)) {
       compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID));
     }
   }
   maybeRewriteClassDefinition(n);
 }
コード例 #13
0
 private void reportExtraRequireWarning(Node call, String require) {
   if (DEFAULT_EXTRA_NAMESPACES.contains(require)) {
     return;
   }
   JSDocInfo jsDoc = call.getJSDocInfo();
   if (jsDoc != null && jsDoc.getSuppressions().contains("extraRequire")) {
     // There is a @suppress {extraRequire} on the call node. Even though the compiler generally
     // doesn't understand @suppress in that position, respect it in this case,
     // since lots of people put it there to suppress the closure-linter's extraRequire check.
     return;
   }
   compiler.report(JSError.make(call, EXTRA_REQUIRE_WARNING, require));
 }
コード例 #14
0
 @Override
 public boolean isModule() {
   checkErrorManager();
   try {
     regenerateDependencyInfoIfNecessary();
     return isModuleFile;
   } catch (IOException e) {
     compiler
         .getErrorManager()
         .report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName()));
     return false;
   }
 }
コード例 #15
0
 private void maybeRewriteClassDefinition(Node n, Node target, Node value) {
   if (isGoogDefineClass(value)) {
     if (!target.isQualifiedName()) {
       compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID));
     }
     ClassDefinition def = extractClassDefinition(target, value);
     if (def != null) {
       value.detachFromParent();
       target.detachFromParent();
       rewriteGoogDefineClass(n, def);
     }
   }
 }
コード例 #16
0
 /** Gets a list of types provided by this input. */
 @Override
 public Collection<String> getProvides() {
   checkErrorManager();
   try {
     regenerateDependencyInfoIfNecessary();
     return Collections.unmodifiableSet(provides);
   } catch (IOException e) {
     compiler
         .getErrorManager()
         .report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName()));
     return ImmutableList.of();
   }
 }
コード例 #17
0
 /** Sets the default values of tweaks based on compiler options. */
 private void applyCompilerDefaultValueOverrides(Map<String, TweakInfo> tweakInfos) {
   for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) {
     String tweakId = entry.getKey();
     TweakInfo tweakInfo = tweakInfos.get(tweakId);
     if (tweakInfo == null) {
       compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId));
     } else {
       TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc;
       Node value = entry.getValue();
       if (!registerFunc.isValidNodeType(value.getType())) {
         compiler.report(
             JSError.make(
                 INVALID_TWEAK_DEFAULT_VALUE_WARNING,
                 tweakId,
                 registerFunc.getName(),
                 registerFunc.getExpectedTypeName()));
       } else {
         tweakInfo.defaultValueNode = value;
       }
     }
   }
 }
コード例 #18
0
  void inferScope(Node n, Scope scope) {
    TypeInference typeInference =
        new TypeInference(
            compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap);
    try {
      typeInference.analyze();

      // Resolve any new type names found during the inference.
      compiler.getTypeRegistry().resolveTypesInScope(scope);

    } catch (DataFlowAnalysis.MaxIterationsExceededException e) {
      compiler.report(JSError.make(n, DATAFLOW_ERROR));
    }
  }
コード例 #19
0
 private void rewriteClassDefinition(Node n, Node parent, NodeTraversal t) {
   if (parent.getParent().isConst() || parent.getParent().isLet()) {
     compiler.report(JSError.make(n, POLYMER_INVALID_DECLARATION));
     return;
   }
   ClassDefinition def = extractClassDefinition(n);
   if (def != null) {
     if (NodeUtil.isNameDeclaration(parent.getParent()) || parent.isAssign()) {
       rewritePolymerClass(parent.getParent(), def, t);
     } else {
       rewritePolymerClass(parent, def, t);
     }
   }
 }
コード例 #20
0
  /**
   * Gets the JSTypeExpression for a given property using its "type" key.
   *
   * @see https://github.com/Polymer/polymer/blob/0.8-preview/PRIMER.md#configuring-properties
   */
  private JSTypeExpression getTypeFromProperty(MemberDefinition property) {
    if (property.info != null && property.info.hasType()) {
      return property.info.getType();
    }

    String typeString = "";
    if (property.value.isObjectLit()) {
      Node typeValue = NodeUtil.getFirstPropMatchingKey(property.value, "type");
      if (typeValue == null || !typeValue.isName()) {
        compiler.report(JSError.make(property.name, POLYMER_INVALID_PROPERTY));
        return null;
      }
      typeString = typeValue.getString();
    } else if (property.value.isName()) {
      typeString = property.value.getString();
    }

    Node typeNode = null;
    switch (typeString) {
      case "Boolean":
      case "String":
      case "Number":
        typeNode = IR.string(typeString.toLowerCase());
        break;
      case "Array":
      case "Function":
      case "Object":
      case "Date":
        typeNode = new Node(Token.BANG, IR.string(typeString));
        break;
      default:
        compiler.report(JSError.make(property.name, POLYMER_INVALID_PROPERTY));
        return null;
    }

    return new JSTypeExpression(typeNode, VIRTUAL_FILE);
  }
コード例 #21
0
    /** Strip property type annotations and add suppress checkTypes and globalThis on functions. */
    private void suppressBehavior(Node behaviorValue) {
      if (behaviorValue == null) {
        compiler.report(JSError.make(behaviorValue, POLYMER_UNQUALIFIED_BEHAVIOR));
        return;
      }

      if (behaviorValue.isArrayLit()) {
        for (Node child : behaviorValue.children()) {
          suppressBehavior(child);
        }
      } else if (behaviorValue.isObjectLit()) {
        stripPropertyTypes(behaviorValue);
        addBehaviorSuppressions(behaviorValue);
      }
    }
コード例 #22
0
  @Override
  public void process(Node externs, Node root) {
    // Find and validate the markers.
    NodeTraversal.traverseEs6(compiler, root, new Callback());

    // Complain about any unmatched markers.
    for (Node node : markerStack) {
      compiler.report(JSError.make(node, UNMATCHED_START_MARKER, startMarkerName));
    }

    // Add the block for the valid marker sets.
    for (Marker marker : validMarkers) {
      addBlocks(marker);
    }
  }
コード例 #23
0
  @Override
  public void process(Node externs, Node root) {
    FindPolymerExterns externsCallback = new FindPolymerExterns();
    NodeTraversal.traverse(compiler, externs, externsCallback);
    polymerElementExterns = externsCallback.polymerElementExterns;
    polymerElementProps = externsCallback.getpolymerElementProps();

    if (polymerElementExterns == null) {
      compiler.report(JSError.make(externs, POLYMER_MISSING_EXTERNS));
      return;
    }

    globalNames = new GlobalNamespace(compiler, externs, root);

    hotSwapScript(root, null);
  }
コード例 #24
0
    /** Processes a GETPROP node. */
    private void handleGetProp(NodeTraversal t, Node n) {
      String name = n.getLastChild().getString();
      T type = typeSystem.getType(getScope(), n.getFirstChild(), name);

      Property prop = getProperty(name);
      if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) {
        if (showInvalidationWarnings) {
          compiler.report(
              JSError.make(
                  t.getSourceName(),
                  n,
                  INVALIDATION,
                  name,
                  (type == null ? "null" : type.toString()),
                  n.toString()));
        }
      }
    }
コード例 #25
0
  /**
   * Creates an intrument functions compiler pass.
   *
   * @param compiler The JSCompiler
   * @param functionNames Assigned function identifiers.
   * @param templateFilename Template filename; for use during error reporting only.
   * @param appNameStr String to pass to appNameSetter.
   * @param readable Instrumentation template protobuf text.
   */
  InstrumentFunctions(
      AbstractCompiler compiler,
      FunctionNames functionNames,
      String templateFilename,
      String appNameStr,
      Readable readable) {
    this.compiler = compiler;
    this.functionNames = functionNames;
    this.templateFilename = templateFilename;
    this.appNameStr = appNameStr;

    Instrumentation.Builder builder = Instrumentation.newBuilder();
    try {
      TextFormat.merge(readable, builder);
    } catch (IOException e) {
      compiler.report(
          JSError.make(
              RhinoErrorReporter.PARSE_ERROR,
              "Error reading instrumentation template protobuf at " + templateFilename));
      this.initCodeSource = "";
      this.definedFunctionName = "";
      this.reportFunctionName = "";
      this.reportFunctionExitName = "";
      this.appNameSetter = "";
      this.declarationsToRemove = Lists.newArrayList();
      return;
    }

    Instrumentation template = builder.build();

    StringBuilder initCodeSourceBuilder = new StringBuilder();
    for (String line : template.getInitList()) {
      initCodeSourceBuilder.append(line).append("\n");
    }
    this.initCodeSource = initCodeSourceBuilder.toString();

    this.definedFunctionName = template.getReportDefined();
    this.reportFunctionName = template.getReportCall();
    this.reportFunctionExitName = template.getReportExit();
    this.appNameSetter = template.getAppNameSetter();

    this.declarationsToRemove = ImmutableList.copyOf(template.getDeclarationToRemoveList());
  }
コード例 #26
0
    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if (isBehavior(n)) {
        if (!n.isVar() && !n.isAssign()) {
          compiler.report(JSError.make(n, POLYMER_UNQUALIFIED_BEHAVIOR));
          return;
        }

        // Add @nocollapse.
        JSDocInfoBuilder newDocs = JSDocInfoBuilder.maybeCopyFrom(n.getJSDocInfo());
        newDocs.recordNoCollapse();
        n.setJSDocInfo(newDocs.build());

        Node behaviorValue = n.getChildAtIndex(1);
        if (n.isVar()) {
          behaviorValue = n.getFirstChild().getFirstChild();
        }
        suppressBehavior(behaviorValue);
      }
    }
コード例 #27
0
  private JSDocInfo mergeJsDocFor(ClassDefinition cls, Node associatedNode) {
    // avoid null checks
    JSDocInfo classInfo = (cls.classInfo != null) ? cls.classInfo : new JSDocInfo(true);

    JSDocInfo ctorInfo =
        (cls.constructor.info != null) ? cls.constructor.info : new JSDocInfo(true);

    Node superNode = cls.superClass;

    // Start with a clone of the constructor info if there is one.
    JSDocInfoBuilder mergedInfo =
        cls.constructor.info != null
            ? JSDocInfoBuilder.copyFrom(ctorInfo)
            : new JSDocInfoBuilder(true);

    // merge block description
    String blockDescription =
        Joiner.on("\n")
            .skipNulls()
            .join(classInfo.getBlockDescription(), ctorInfo.getBlockDescription());
    if (!blockDescription.isEmpty()) {
      mergedInfo.recordBlockDescription(blockDescription);
    }

    // merge suppressions
    Set<String> suppressions = Sets.newHashSet();
    suppressions.addAll(classInfo.getSuppressions());
    suppressions.addAll(ctorInfo.getSuppressions());
    if (!suppressions.isEmpty()) {
      mergedInfo.recordSuppressions(suppressions);
    }

    // Use class deprecation if set.
    if (classInfo.isDeprecated()) {
      mergedInfo.recordDeprecated();
    }

    String deprecationReason = null;
    if (classInfo.getDeprecationReason() != null) {
      deprecationReason = classInfo.getDeprecationReason();
      mergedInfo.recordDeprecationReason(deprecationReason);
    }

    // Use class visibility if specifically set
    Visibility visibility = classInfo.getVisibility();
    if (visibility != null && visibility != JSDocInfo.Visibility.INHERITED) {
      mergedInfo.recordVisibility(classInfo.getVisibility());
    }

    if (classInfo.isConstant()) {
      mergedInfo.recordConstancy();
    }

    if (classInfo.isExport()) {
      mergedInfo.recordExport();
    }

    // If @ngInject is on the ctor, it's already been copied above.
    if (classInfo.isNgInject()) {
      compiler.report(JSError.make(associatedNode, GOOG_CLASS_NG_INJECT_ON_CLASS));
      mergedInfo.recordNgInject(true);
    }

    // @constructor is implied, @interface must be explicit
    boolean isInterface = classInfo.isInterface() || ctorInfo.isInterface();
    if (isInterface) {
      mergedInfo.recordInterface();
      List<JSTypeExpression> extendedInterfaces = null;
      if (classInfo.getExtendedInterfacesCount() > 0) {
        extendedInterfaces = classInfo.getExtendedInterfaces();
      } else if (ctorInfo.getExtendedInterfacesCount() == 0 && superNode != null) {
        extendedInterfaces =
            ImmutableList.of(
                new JSTypeExpression(
                    new Node(Token.BANG, IR.string(superNode.getQualifiedName())), VIRTUAL_FILE));
      }
      if (extendedInterfaces != null) {
        for (JSTypeExpression extend : extendedInterfaces) {
          mergedInfo.recordExtendedInterface(extend);
        }
      }
    } else {
      // @constructor by default
      mergedInfo.recordConstructor();
      if (classInfo.makesUnrestricted() || ctorInfo.makesUnrestricted()) {
        mergedInfo.recordUnrestricted();
      } else if (classInfo.makesDicts() || ctorInfo.makesDicts()) {
        mergedInfo.recordDict();
      } else {
        // @struct by default
        mergedInfo.recordStruct();
      }

      if (classInfo.getBaseType() != null) {
        mergedInfo.recordBaseType(classInfo.getBaseType());
      } else if (superNode != null) {
        // a "super" implies @extends, build a default.
        JSTypeExpression baseType =
            new JSTypeExpression(
                new Node(Token.BANG, IR.string(superNode.getQualifiedName())), VIRTUAL_FILE);
        mergedInfo.recordBaseType(baseType);
      }

      // @implements from the class if they exist
      List<JSTypeExpression> interfaces = classInfo.getImplementedInterfaces();
      for (JSTypeExpression implemented : interfaces) {
        mergedInfo.recordImplementedInterface(implemented);
      }
    }

    // merge @template types if they exist
    List<String> templateNames = new ArrayList<>();
    templateNames.addAll(classInfo.getTemplateTypeNames());
    templateNames.addAll(ctorInfo.getTemplateTypeNames());
    for (String typeName : templateNames) {
      mergedInfo.recordTemplateTypeName(typeName);
    }
    return mergedInfo.build(associatedNode);
  }
コード例 #28
0
  /**
   * Validates the class definition and if valid, destructively extracts the class definition from
   * the AST.
   */
  private ClassDefinition extractClassDefinition(Node targetName, Node callNode) {

    JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(targetName);

    // name = goog.defineClass(superClass, {...}, [modifier, ...])
    Node superClass = NodeUtil.getArgumentForCallOrNew(callNode, 0);
    if (superClass == null || (!superClass.isNull() && !superClass.isQualifiedName())) {
      compiler.report(JSError.make(callNode, GOOG_CLASS_SUPER_CLASS_NOT_VALID));
      return null;
    }

    if (NodeUtil.isNullOrUndefined(superClass) || superClass.matchesQualifiedName("Object")) {
      superClass = null;
    }

    Node description = NodeUtil.getArgumentForCallOrNew(callNode, 1);
    if (description == null || !description.isObjectLit() || !validateObjLit(description)) {
      // report bad class definition
      compiler.report(JSError.make(callNode, GOOG_CLASS_DESCRIPTOR_NOT_VALID));
      return null;
    }

    int paramCount = callNode.getChildCount() - 1;
    if (paramCount > 2) {
      compiler.report(JSError.make(callNode, GOOG_CLASS_UNEXPECTED_PARAMS));
      return null;
    }

    Node constructor = extractProperty(description, "constructor");
    if (classInfo != null && classInfo.isInterface()) {
      if (constructor != null) {
        compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_ON_INTERFACE));
        return null;
      }
    } else if (constructor == null) {
      // report missing constructor
      compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_MISSING));
      return null;
    }
    if (constructor == null) {
      constructor =
          IR.function(
              IR.name("").srcref(callNode),
              IR.paramList().srcref(callNode),
              IR.block().srcref(callNode));
      constructor.srcref(callNode);
    }

    JSDocInfo info = NodeUtil.getBestJSDocInfo(constructor);

    Node classModifier = null;
    Node statics = null;
    Node staticsProp = extractProperty(description, "statics");
    if (staticsProp != null) {
      if (staticsProp.isObjectLit() && validateObjLit(staticsProp)) {
        statics = staticsProp;
      } else if (staticsProp.isFunction()) {
        classModifier = staticsProp;
      } else {
        compiler.report(JSError.make(staticsProp, GOOG_CLASS_STATICS_NOT_VALID));
        return null;
      }
    }

    if (statics == null) {
      statics = IR.objectlit();
    }

    // Ok, now rip apart the definition into its component pieces.
    // Remove the "special" property key nodes.
    maybeDetach(constructor.getParent());
    maybeDetach(statics.getParent());
    if (classModifier != null) {
      maybeDetach(classModifier.getParent());
    }
    ClassDefinition def =
        new ClassDefinition(
            targetName,
            classInfo,
            maybeDetach(superClass),
            new MemberDefinition(info, null, maybeDetach(constructor)),
            objectLitToList(maybeDetach(statics)),
            objectLitToList(description),
            maybeDetach(classModifier));
    return def;
  }
コード例 #29
0
 /** @param requirement */
 private static void reportInvalidRequirement(
     AbstractCompiler compiler, Requirement requirement, String reason) {
   compiler.report(
       JSError.make(INVALID_REQUIREMENT_SPEC, reason, TextFormat.printToString(requirement)));
 }
コード例 #30
0
  public void testOrderingDescription() throws Exception {
    JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE);
    JSError e2 = JSError.make(NULL_SOURCE, -1, -1, JOO_TYPE);

    assertSmaller(error(e1), error(e2));
  }